ContactsProvider2.java revision 8ead0dc62d0031a22af0d14c7ed05893507893c9
14f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/*
24f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Copyright (C) 2009 The Android Open Source Project
34f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton *
44f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Licensed under the Apache License, Version 2.0 (the "License");
54f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * you may not use this file except in compliance with the License.
64f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * You may obtain a copy of the License at
74f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton *
84f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton *      http://www.apache.org/licenses/LICENSE-2.0
94f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton *
104f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Unless required by applicable law or agreed to in writing, software
114f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * distributed under the License is distributed on an "AS IS" BASIS,
124f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * See the License for the specific language governing permissions and
144f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * limitations under the License
154f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1728f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarpackage com.android.providers.contacts;
1828f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar
1953214b3ed12b0ff9cb589b6559311f2ac142f2e3Bjorn Bringertimport com.android.common.content.SyncStateContentProviderHelper;
205b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikovimport com.android.providers.contacts.ContactAggregator.AggregationSuggestionParameter;
2197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactLookupKey.LookupKeySegment;
2297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.AggregatedPresenceColumns;
2397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns;
2497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.Clauses;
2597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.ContactsColumns;
2697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.ContactsStatusUpdatesColumns;
2797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
2871340347b4862d4b1368a5d69d1667e2245952e4Daisuke Miyakawaimport com.android.providers.contacts.ContactsDatabaseHelper.DataUsageStatColumns;
2997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
3023ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onukiimport com.android.providers.contacts.ContactsDatabaseHelper.Joins;
312f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawaimport com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns;
3297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupColumns;
3397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
3497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneColumns;
3597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
361dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoroimport com.android.providers.contacts.ContactsDatabaseHelper.PhotoFilesColumns;
3797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
3897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
3903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.SearchIndexColumns;
4097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.SettingsColumns;
4197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.StatusUpdatesColumns;
423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmannimport com.android.providers.contacts.ContactsDatabaseHelper.StreamItemPhotosColumns;
43f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport com.android.providers.contacts.ContactsDatabaseHelper.StreamItemsColumns;
4497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.Tables;
45ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmannimport com.android.providers.contacts.ContactsDatabaseHelper.Views;
46d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmannimport com.android.providers.contacts.SearchIndexManager.FtsQueryBuilder;
472f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawaimport com.android.providers.contacts.util.DbQueryUtils;
4897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.vcard.VCardComposer;
4997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.vcard.VCardConfig;
5097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Lists;
5197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Maps;
5297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Sets;
53f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawaimport com.google.common.annotations.VisibleForTesting;
5497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
55b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.accounts.Account;
56caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikovimport android.accounts.AccountManager;
575b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanaimport android.accounts.OnAccountsUpdateListener;
58bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.Notification;
59bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.NotificationManager;
60bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.PendingIntent;
61c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.app.SearchManager;
62568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderOperation;
63568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderResult;
646ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.content.ContentResolver;
6535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.ContentUris;
6667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.ContentValues;
6767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.Context;
68627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.content.IContentService;
69bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.content.Intent;
70568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.OperationApplicationException;
713d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.content.SharedPreferences;
72627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.content.SyncAdapterType;
7367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.UriMatcher;
740bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmannimport android.content.pm.PackageManager;
750bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmannimport android.content.pm.PackageManager.NameNotFoundException;
765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoroimport android.content.pm.ProviderInfo;
77f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringertimport android.content.res.AssetFileDescriptor;
783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmannimport android.content.res.Resources;
790bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmannimport android.content.res.Resources.NotFoundException;
80409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onukiimport android.database.AbstractCursor;
81e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikovimport android.database.CrossProcessCursor;
824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.Cursor;
83e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikovimport android.database.CursorWindow;
84ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.database.CursorWrapper;
85ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.database.DatabaseUtils;
8609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor;
8709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor.RowBuilder;
884f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteDatabase;
8908ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwoodimport android.database.sqlite.SQLiteDoneException;
904f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteQueryBuilder;
91f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.graphics.Bitmap;
92f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.graphics.BitmapFactory;
934f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.net.Uri;
94d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.net.Uri.Builder;
95c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoroimport android.os.AsyncTask;
96bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Binder;
976ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.os.Bundle;
98bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Handler;
99bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.HandlerThread;
100bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Message;
101ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringertimport android.os.ParcelFileDescriptor;
102c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoroimport android.os.ParcelFileDescriptor.AutoCloseInputStream;
103bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Process;
104b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.os.RemoteException;
10515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikovimport android.os.StrictMode;
1060dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikovimport android.os.SystemClock;
1070e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport android.os.SystemProperties;
1083d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.preference.PreferenceManager;
109508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkeyimport android.provider.BaseColumns;
1103de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract;
111b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions;
11282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoroimport android.provider.ContactsContract.Authorization;
11397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Email;
11497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
11597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Im;
11697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Nickname;
1176d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Note;
11897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Organization;
11997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Phone;
12097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Photo;
1214928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.SipAddress;
12297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
12397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
124ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.provider.ContactsContract.ContactCounts;
1253de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Contacts;
1265b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikovimport android.provider.ContactsContract.Contacts.AggregationSuggestions;
1273de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Data;
12871340347b4862d4b1368a5d69d1667e2245952e4Daisuke Miyakawaimport android.provider.ContactsContract.DataUsageFeedback;
129d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.provider.ContactsContract.Directory;
130f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.provider.ContactsContract.DisplayPhoto;
1313de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Groups;
132bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.provider.ContactsContract.Intents;
1333de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.PhoneLookup;
1341dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoroimport android.provider.ContactsContract.PhotoFiles;
1350c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoroimport android.provider.ContactsContract.Profile;
13609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.provider.ContactsContract.ProviderStatus;
1373de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.RawContacts;
1383711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenenimport android.provider.ContactsContract.RawContactsEntity;
139916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikovimport android.provider.ContactsContract.SearchSnippetColumns;
1403de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Settings;
14182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikovimport android.provider.ContactsContract.StatusUpdates;
1423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmannimport android.provider.ContactsContract.StreamItemPhotos;
143f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.provider.ContactsContract.StreamItems;
14497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.OpenableColumns;
14597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.SyncStateContract;
146a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.telephony.PhoneNumberUtils;
1479a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikovimport android.telephony.TelephonyManager;
148a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.text.TextUtils;
149c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.util.Log;
1504f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
151108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawaimport java.io.BufferedWriter;
152d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.ByteArrayOutputStream;
153f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport java.io.File;
154b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport java.io.FileNotFoundException;
155d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.IOException;
156d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.OutputStream;
157108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawaimport java.io.OutputStreamWriter;
158108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawaimport java.io.Writer;
159d0eb93009559d095de0448907527aeb059801dc4Dave Santoroimport java.security.SecureRandom;
16042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.text.SimpleDateFormat;
1617e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport java.util.ArrayList;
16246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawaimport java.util.Arrays;
1635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.Collections;
16442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.util.Date;
165b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport java.util.HashMap;
1660e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.HashSet;
1675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.List;
168622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkeyimport java.util.Locale;
169b5a4add17815167d20a90645779df34cdf45280dFred Quintanaimport java.util.Map;
1700e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.Set;
171ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikovimport java.util.concurrent.CountDownLatch;
1724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/**
1744f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Contacts content provider. The contract between this provider and applications
1754f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * is defined in {@link ContactsContract}.
1764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
177078f588cef389358adabc579de00747878f3c108Dave Santoropublic class ContactsProvider2 extends AbstractContactsProvider
178078f588cef389358adabc579de00747878f3c108Dave Santoro        implements OnAccountsUpdateListener {
179caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
180bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final String TAG = "ContactsProvider";
181bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov
182bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE);
1834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
18415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_INITIALIZE = 0;
18515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_OPEN_WRITE_ACCESS = 1;
18615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS = 2;
18715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_ACCOUNTS = 3;
18815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_LOCALE = 4;
18915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM = 5;
19005e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_SEARCH_INDEX = 6;
19105e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_PROVIDER_STATUS = 7;
19205e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_DIRECTORIES = 8;
19305e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_CHANGE_LOCALE = 9;
194f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int BACKGROUND_TASK_CLEANUP_PHOTOS = 10;
195619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Default for the maximum number of returned aggregation suggestions. */
1973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DEFAULT_MAX_SUGGESTIONS = 5;
1983cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /** Limit for the maximum number of social stream items to store under a raw contact. */
2003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int MAX_STREAM_ITEMS_PER_RAW_CONTACT = 5;
2013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
202f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /** Rate limit (in ms) for photo cleanup.  Do it at most once per day. */
203f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int PHOTO_CLEANUP_RATE_LIMIT = 24 * 60 * 60 * 1000;
204f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
2053d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /**
20682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * Default expiration duration for pre-authorized URIs.  May be overridden from a secure
20782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * setting.
20882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     */
20982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    private static final int DEFAULT_PREAUTHORIZED_URI_EXPIRATION = 5 * 60 * 1000;
21082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
21182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    /**
21282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * Random URI parameter that will be appended to preauthorized URIs for uniqueness.
21382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     */
21482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    private static final String PREAUTHORIZED_URI_TOKEN = "perm_token";
21582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
21682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    /**
217b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov     * Property key for the legacy contact import version. The need for a version
2183d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * as opposed to a boolean flag is that if we discover bugs in the contact import process,
2193d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * we can trigger re-import by incrementing the import version.
2203d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     */
221b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final String PROPERTY_CONTACTS_IMPORTED = "contacts_imported_v1";
222b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final int PROPERTY_CONTACTS_IMPORT_VERSION = 1;
22351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    private static final String PREF_LOCALE = "locale";
2243d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2250dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static final String PROPERTY_AGGREGATION_ALGORITHM = "aggregation_v2";
2260dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static final int PROPERTY_AGGREGATION_ALGORITHM_VERSION = 2;
2270dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
2280e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff    private static final String AGGREGATE_CONTACTS = "sync.contacts.aggregate";
2290e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff
2305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private static final ProfileAwareUriMatcher sUriMatcher =
2315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            new ProfileAwareUriMatcher(UriMatcher.NO_MATCH);
2324f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2332f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    /**
2342f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     * Used to insert a column into strequent results, which enables SQL to sort the list using
2352f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     * the total times contacted. See also {@link #sStrequentFrequentProjectionMap}.
2362f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     */
2372f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    private static final String TIMES_USED_SORT_COLUMN = "times_used_sort";
2385e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
239d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final String STREQUENT_ORDER_BY = Contacts.STARRED + " DESC, "
2402f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            + TIMES_USED_SORT_COLUMN + " DESC, "
2419b43551f1ce33b79141772737a262ce609bd0cebMegha Joshi            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
242d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final String STREQUENT_LIMIT =
243d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            "(SELECT COUNT(1) FROM " + Tables.CONTACTS + " WHERE "
244d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            + Contacts.STARRED + "=1) + 25";
245d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
24645ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa    private static final String FREQUENT_ORDER_BY = DataUsageStatColumns.TIMES_USED + " DESC,"
24745ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
24845ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa
2496e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_CONTACTS_TABLE =
2509b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.CONTACTS + " SET " + Contacts.TIMES_CONTACTED + "=" +
2519b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + Contacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
2529b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + Contacts.TIMES_CONTACTED + " + 1) END WHERE " + Contacts._ID + "=?";
2539b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
2546e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE =
2559b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.RAW_CONTACTS + " SET " + RawContacts.TIMES_CONTACTED + "=" +
2569b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + RawContacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
2579b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + RawContacts.TIMES_CONTACTED + " + 1) END WHERE " + RawContacts.CONTACT_ID + "=?";
2589b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
259de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa    /* package */ static final String PHONEBOOK_COLLATOR_NAME = "PHONEBOOK";
260de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa
2613716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    // Regex for splitting query strings - we split on any group of non-alphanumeric characters,
2623716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    // excluding the @ symbol.
2633716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    /* package */ static final String QUERY_TOKENIZER_REGEX = "[^\\w@]+";
2643716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
265d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS = 1000;
266d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS_ID = 1001;
2675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP = 1002;
2685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID = 1003;
269a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_DATA = 1004;
2705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_FILTER = 1005;
2715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT = 1006;
2725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT_FILTER = 1007;
2735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_GROUP = 1008;
274a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_PHOTO = 1009;
275bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_PHOTO = 1010;
276bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_PHOTO = 1011;
277bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_ID_DISPLAY_PHOTO = 1012;
278bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_DISPLAY_PHOTO = 1013;
279bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_DISPLAY_PHOTO = 1014;
280bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_AS_VCARD = 1015;
281bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_AS_MULTI_VCARD = 1016;
282bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_DATA = 1017;
283bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_DATA = 1018;
284bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_ID_ENTITIES = 1019;
285bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ENTITIES = 1020;
286bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_ENTITIES = 1021;
287bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_ID_STREAM_ITEMS = 1022;
288bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_STREAM_ITEMS = 1023;
289bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_STREAM_ITEMS = 1024;
290bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_FREQUENT = 1025;
2914f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2925ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS = 2002;
2935ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_ID = 2003;
2945ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_DATA = 2004;
29546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITY_ID = 2005;
296f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int RAW_CONTACTS_ID_DISPLAY_PHOTO = 2006;
297f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int RAW_CONTACTS_ID_STREAM_ITEMS = 2007;
29882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro    private static final int RAW_CONTACTS_ID_STREAM_ITEMS_ID = 2008;
2994f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
3006bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
3016bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
302ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
30348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_ID = 3003;
30448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_FILTER = 3004;
30548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS = 3005;
30648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_ID = 3006;
30748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_LOOKUP = 3007;
30848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_FILTER = 3008;
30948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS = 3009;
31048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS_ID = 3010;
311a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
3126bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
3136bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
314b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
315b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
316b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
31782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES = 7000;
31882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES_ID = 7001;
3191f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
32031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
32131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
322eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final int SETTINGS = 9000;
323eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
324ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
325ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
326ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
327ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
32835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
329b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private static final int SYNCSTATE_ID = 11001;
3305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private static final int PROFILE_SYNCSTATE = 11002;
3315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private static final int PROFILE_SYNCSTATE_ID = 11003;
33235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
333c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SUGGESTIONS = 12001;
334c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SHORTCUT = 12002;
335c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
33646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITIES = 15001;
33746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
33809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private static final int PROVIDER_STATUS = 16001;
33909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
340d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES = 17001;
341d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES_ID = 17002;
342d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3437a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private static final int COMPLETE_NAME = 18000;
3447a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
34524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE = 19000;
34624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_ENTITIES = 19001;
34724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_DATA = 19002;
34824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_DATA_ID = 19003;
34924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_AS_VCARD = 19004;
35024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS = 19005;
35124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID = 19006;
35224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID_DATA = 19007;
35324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID_ENTITIES = 19008;
3545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private static final int PROFILE_STATUS_UPDATES = 19009;
3553202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro    private static final int PROFILE_RAW_CONTACT_ENTITIES = 19010;
35685077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro    private static final int PROFILE_PHOTO = 19011;
35785077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro    private static final int PROFILE_DISPLAY_PHOTO = 19012;
35824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
35946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final int DATA_USAGE_FEEDBACK_ID = 20001;
36046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
3613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS = 21000;
3623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_PHOTOS = 21001;
3633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID = 21002;
3643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID_PHOTOS = 21003;
3653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID_PHOTOS_ID = 21004;
3663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_LIMIT = 21005;
3673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
368f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int DISPLAY_PHOTO = 22000;
369f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int PHOTO_DIMENSIONS = 22001;
370f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
3715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Inserts into URIs in this map will direct to the profile database if the parent record's
3725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // value (looked up from the ContentValues object with the key specified by the value in this
3735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // map) is in the profile ID-space (see {@link ProfileDatabaseHelper#PROFILE_ID_SPACE}).
3745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private static final Map<Integer, String> INSERT_URI_ID_VALUE_MAP = Maps.newHashMap();
3755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    static {
3765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(DATA, Data.RAW_CONTACT_ID);
3775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(RAW_CONTACTS_DATA, Data.RAW_CONTACT_ID);
3785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(STATUS_UPDATES, StatusUpdates.DATA_ID);
3795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(STREAM_ITEMS, StreamItems.RAW_CONTACT_ID);
3805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(RAW_CONTACTS_ID_STREAM_ITEMS, StreamItems.RAW_CONTACT_ID);
3815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(STREAM_ITEMS_PHOTOS, StreamItemPhotos.STREAM_ITEM_ID);
3825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(STREAM_ITEMS_ID_PHOTOS, StreamItemPhotos.STREAM_ITEM_ID);
3835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
3845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
38536612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    // Any interactions that involve these URIs will also require the calling package to have either
38636612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    // android.permission.READ_SOCIAL_STREAM permission or android.permission.WRITE_SOCIAL_STREAM
38736612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    // permission, depending on the type of operation being performed.
38836612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    private static final List<Integer> SOCIAL_STREAM_URIS = Lists.newArrayList(
38936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            CONTACTS_ID_STREAM_ITEMS,
39036612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            CONTACTS_LOOKUP_STREAM_ITEMS,
39136612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            CONTACTS_LOOKUP_ID_STREAM_ITEMS,
39236612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            RAW_CONTACTS_ID_STREAM_ITEMS,
39336612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            RAW_CONTACTS_ID_STREAM_ITEMS_ID,
39436612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            STREAM_ITEMS,
39536612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            STREAM_ITEMS_PHOTOS,
39636612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            STREAM_ITEMS_ID,
39736612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            STREAM_ITEMS_ID_PHOTOS,
39836612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            STREAM_ITEMS_ID_PHOTOS_ID
39936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    );
40036612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
401dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID =
402dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
403dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME
404dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
405dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE
40643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AND ("
40743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + GroupsColumns.CONCRETE_DATA_SET
40843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + "=" + RawContactsColumns.CONCRETE_DATA_SET + " OR "
40943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + GroupsColumns.CONCRETE_DATA_SET + " IS NULL AND "
41043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + RawContactsColumns.CONCRETE_DATA_SET + " IS NULL)"
411dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " AND " + Groups.FAVORITES + " != 0";
412dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
413dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID =
414dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
415dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
416dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
417dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
41843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AND ("
41943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + GroupsColumns.CONCRETE_DATA_SET + "="
42043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + RawContactsColumns.CONCRETE_DATA_SET + " OR "
42143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + GroupsColumns.CONCRETE_DATA_SET + " IS NULL AND "
42243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + RawContactsColumns.CONCRETE_DATA_SET + " IS NULL)"
42343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + " AND " + Groups.AUTO_ADD + " != 0";
424dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
425dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String[] PROJECTION_GROUP_ID
426dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            = new String[]{Tables.GROUPS + "." + Groups._ID};
427dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
428dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_GROUPMEMBERSHIP_DATA = DataColumns.MIMETYPE_ID + "=? "
429dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.GROUP_ROW_ID + "=? "
430dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.RAW_CONTACT_ID + "=?";
431dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
432dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_STARRED_FROM_RAW_CONTACTS =
433dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            "SELECT " + RawContacts.STARRED
434dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " FROM " + Tables.RAW_CONTACTS + " WHERE " + RawContacts._ID + "=?";
435dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
436d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private interface DataContactsQuery {
437f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        public static final String TABLE = "data "
438f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) "
439f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN contacts ON (raw_contacts.contact_id = contacts._id)";
44067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
44167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
4426cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
4436802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            RawContactsColumns.CONCRETE_ACCOUNT_TYPE,
4446802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            RawContactsColumns.CONCRETE_ACCOUNT_NAME,
44543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            RawContactsColumns.CONCRETE_DATA_SET,
4463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
447f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            ContactsColumns.CONCRETE_ID
448ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
449ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
450d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
4516802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        public static final int ACCOUNT_TYPE = 1;
4526802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        public static final int ACCOUNT_NAME = 2;
45343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        public static final int DATA_SET = 3;
45443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        public static final int DATA_ID = 4;
45543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        public static final int CONTACT_ID = 5;
456ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
4571f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
458f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov    interface RawContactsQuery {
45919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String TABLE = Tables.RAW_CONTACTS;
46019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
46119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String[] COLUMNS = new String[] {
462ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.DELETED,
463ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
464ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
46543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.DATA_SET,
46619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        };
46719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
46819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int DELETED = 0;
469ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_TYPE = 1;
470ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_NAME = 2;
47143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int DATA_SET = 3;
47219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    }
47319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
474c76cdd0723b99f478c9ba5329d14a971cd8dfb3dCostin Manolache    public static final String DEFAULT_ACCOUNT_TYPE = "com.google";
475caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
47671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    /** Sql where statement for filtering on groups. */
47771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private static final String CONTACTS_IN_GROUP_SELECT =
47871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov            Contacts._ID + " IN "
47971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + "(SELECT " + RawContacts.CONTACT_ID
48071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " FROM " + Tables.RAW_CONTACTS
48171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + " IN "
48271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
48371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " FROM " + Tables.DATA_JOIN_MIMETYPES
4847cf50494501938f175d288077145acf49da8f171Daniel Lehmann                            + " WHERE " + DataColumns.MIMETYPE_ID + "=?"
4857cf50494501938f175d288077145acf49da8f171Daniel Lehmann                                    + " AND " + GroupMembership.GROUP_ROW_ID + "="
48671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "(SELECT " + Tables.GROUPS + "." + Groups._ID
48771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " FROM " + Tables.GROUPS
48871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " WHERE " + Groups.TITLE + "=?)))";
48971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov
490a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating DIRTY flag on multiple raw contacts */
491a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_DIRTY_SQL =
492a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
493a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.DIRTY + "=1" +
494a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
495a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
496a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating VERSION on multiple raw contacts */
497a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_VERSION_SQL =
498a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
499a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.VERSION + " = " + RawContacts.VERSION + " + 1" +
500a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
501a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
502c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Current contacts - those contacted within the last 3 days (in seconds)
503c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_CURRENT = 3 * 24 * 60 * 60;
504c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
505c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Recent contacts - those contacted within the last 30 days (in seconds)
506c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_RECENT = 30 * 24 * 60 * 60;
507c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
508f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa    private static final String TIME_SINCE_LAST_USED =
509f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa            "(strftime('%s', 'now') - " + DataUsageStatColumns.LAST_TIME_USED + "/1000)";
510f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa
511c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    /*
512c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * Sorting order for email address suggestions: first starred, then the rest.
5132262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * second in_visible_group, then the rest.
5142262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * Within the four (starred/unstarred, in_visible_group/not-in_visible_group) groups
5152262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * - three buckets: very recently contacted, then fairly
516c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * recently contacted, then the rest.  Within each of the bucket - descending count
51746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * of times contacted (both for data row and for contact row). If all else fails, alphabetical.
51846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * (Super)primary email address is returned before other addresses for the same contact.
519c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     */
520c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final String EMAIL_FILTER_SORT_ORDER =
5212262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa        Contacts.STARRED + " DESC, "
5222262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa        + Contacts.IN_VISIBLE_GROUP + " DESC, "
523f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa        + "(CASE WHEN " + TIME_SINCE_LAST_USED + " < " + EMAIL_FILTER_CURRENT
52446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " THEN 0 "
525f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa                + " WHEN " + TIME_SINCE_LAST_USED + " < " + EMAIL_FILTER_RECENT
52646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " THEN 1 "
52746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " ELSE 2 END), "
52846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + DataUsageStatColumns.TIMES_USED + " DESC, "
52946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + Contacts.DISPLAY_NAME + ", "
53046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + Data.CONTACT_ID + ", "
531c591cc2ffecdd0038f787a133606752752294c13Daisuke Miyakawa        + Data.IS_SUPER_PRIMARY + " DESC, "
532c591cc2ffecdd0038f787a133606752752294c13Daisuke Miyakawa        + Data.IS_PRIMARY + " DESC";
53346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
53446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /** Currently same as {@link #EMAIL_FILTER_SORT_ORDER} */
53546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final String PHONE_FILTER_SORT_ORDER = EMAIL_FILTER_SORT_ORDER;
536c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
537916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Name lookup types used for contact filtering */
538916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private static final String CONTACT_LOOKUP_NAME_TYPES =
539916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NAME_COLLATION_KEY + "," +
540916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.EMAIL_BASED_NICKNAME + "," +
54192ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov            NameLookupType.NICKNAME;
542916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
543f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    /**
544f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * If any of these columns are used in a Data projection, there is no point in
545f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * using the DISTINCT keyword, which can negatively affect performance.
546f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     */
547f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    private static final String[] DISTINCT_DATA_PROHIBITING_COLUMNS = {
548f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data._ID,
549f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.RAW_CONTACT_ID,
550f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.NAME_RAW_CONTACT_ID,
551f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_NAME,
552f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_TYPE,
55343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            RawContacts.DATA_SET,
55443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
555f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.DIRTY,
556f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.NAME_VERIFIED,
557f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.SOURCE_ID,
558f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.VERSION,
559f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    };
560916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
561f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsColumns = ProjectionMap.builder()
562f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CUSTOM_RINGTONE)
563f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME)
564f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_ALTERNATIVE)
565f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_SOURCE)
566f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.IN_VISIBLE_GROUP)
567f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LAST_TIME_CONTACTED)
568f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LOOKUP_KEY)
569f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME)
570f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME_STYLE)
571f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHOTO_ID)
572f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            .add(Contacts.PHOTO_FILE_ID)
5733d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_URI)
5743d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_THUMBNAIL_URI)
575f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SEND_TO_VOICEMAIL)
576f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_ALTERNATIVE)
577f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_PRIMARY)
578f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.STARRED)
579f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.TIMES_CONTACTED)
580cf832869bcf91b8037d8b7f510a3a213b30764a3Dmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
581f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
582f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
583f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsPresenceColumns = ProjectionMap.builder()
584f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
585f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE)
586f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
587f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
588f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
589f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
590f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
591f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
592f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
593f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
594f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
595f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
596f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
597f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
598f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
599f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
600f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSnippetColumns = ProjectionMap.builder()
60103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET)
602f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
603f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
604f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactColumns = ProjectionMap.builder()
605f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_NAME)
606f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_TYPE)
60743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(RawContacts.DATA_SET)
60843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(RawContacts.ACCOUNT_TYPE_AND_DATA_SET)
609f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DIRTY)
610f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.NAME_VERIFIED)
611f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SOURCE_ID)
612f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.VERSION)
613f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
614f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
615f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactSyncColumns = ProjectionMap.builder()
616f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC1)
617f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC2)
618f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC3)
619f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC4)
620f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
621f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
622f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataColumns = ProjectionMap.builder()
623f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA1)
624f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA2)
625f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA3)
626f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA4)
627f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA5)
628f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA6)
629f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA7)
630f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA8)
631f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA9)
632f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA10)
633f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA11)
634f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA12)
635f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA13)
636f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA14)
637f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA15)
638f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA_VERSION)
639f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_PRIMARY)
640f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_SUPER_PRIMARY)
641f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.MIMETYPE)
642f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RES_PACKAGE)
643f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC1)
644f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC2)
645f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC3)
646f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC4)
647f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(GroupMembership.GROUP_SOURCE_ID)
648f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
649f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
650f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactPresenceColumns = ProjectionMap.builder()
651f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
652f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.PRESENCE)
653f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
654f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.CHAT_CAPABILITY)
655f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
656f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
657f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
658f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
659f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
660f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
661f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
662f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
663f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
664f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
665f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
666f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
667f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataPresenceColumns = ProjectionMap.builder()
668f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.PRESENCE, Tables.PRESENCE + "." + StatusUpdates.PRESENCE)
669f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CHAT_CAPABILITY, Tables.PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
670f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS, StatusUpdatesColumns.CONCRETE_STATUS)
671f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_TIMESTAMP, StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
672f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_RES_PACKAGE, StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
673f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_LABEL, StatusUpdatesColumns.CONCRETE_STATUS_LABEL)
674f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_ICON, StatusUpdatesColumns.CONCRETE_STATUS_ICON)
675f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
676f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
677038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    /** Contains just BaseColumns._COUNT */
678f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sCountProjectionMap = ProjectionMap.builder()
679f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(BaseColumns._COUNT, "COUNT(*)")
680f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
681f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
682e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    /** Contains just the contacts columns */
683f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionMap = ProjectionMap.builder()
684f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts._ID)
685f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
686f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.NAME_RAW_CONTACT_ID)
68724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(Contacts.IS_USER_PROFILE)
688f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
689f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsPresenceColumns)
690f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
691f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
692916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Contains just the contacts columns */
693f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionWithSnippetMap = ProjectionMap.builder()
694f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
695f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sSnippetColumns)
696f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
697916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
6985e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    /** Used for pushing starred contacts to the top of a times contacted list **/
699f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentStarredProjectionMap = ProjectionMap.builder()
700f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
7012f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, String.valueOf(Long.MAX_VALUE))
702f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
703f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
704f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentFrequentProjectionMap = ProjectionMap.builder()
705f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
7062f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, "SUM(" + DataUsageStatColumns.CONCRETE_TIMES_USED + ")")
707f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
708f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
7094928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    /**
7104928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * Used for Strequent Uri with {@link ContactsContract#STREQUENT_PHONE_ONLY}, which allows
7114928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * users to obtain part of Data columns. Right now Starred part just returns NULL for
7124928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * those data columns (frequent part should return real ones in data table).
7134928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     **/
7144928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    private static final ProjectionMap sStrequentPhoneOnlyStarredProjectionMap
7154928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            = ProjectionMap.builder()
7164928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .addAll(sContactsProjectionMap)
7174928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, String.valueOf(Long.MAX_VALUE))
7184928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.NUMBER, "NULL")
7194928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.TYPE, "NULL")
7204928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.LABEL, "NULL")
7214928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .build();
7224928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
7234928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    /**
7244928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * Used for Strequent Uri with {@link ContactsContract#STREQUENT_PHONE_ONLY}, which allows
7254928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * users to obtain part of Data columns. We hard-code {@link Contacts#IS_USER_PROFILE} to NULL,
7264928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * because sContactsProjectionMap specifies a field that doesn't exist in the view behind the
7274928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * query that uses this projection map.
7284928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     **/
7294928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    private static final ProjectionMap sStrequentPhoneOnlyFrequentProjectionMap
7304928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            = ProjectionMap.builder()
7314928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .addAll(sContactsProjectionMap)
7324928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, DataUsageStatColumns.CONCRETE_TIMES_USED)
7334928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.NUMBER)
7344928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.TYPE)
7354928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.LABEL)
7364928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Contacts.IS_USER_PROFILE, "NULL")
7374928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .build();
7384928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
739f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    /** Contains just the contacts vCard columns */
740f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsVCardProjectionMap = ProjectionMap.builder()
741fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            .add(Contacts._ID)
742f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.DISPLAY_NAME, Contacts.DISPLAY_NAME + " || '.vcf'")
743f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.SIZE, "NULL")
744f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
745f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
746ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    /** Contains just the raw contacts columns */
747f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactsProjectionMap = ProjectionMap.builder()
748f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
749f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
750f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
751f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_PRIMARY)
752f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_ALTERNATIVE)
753f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_SOURCE)
754f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME)
755f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME_STYLE)
756f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_PRIMARY)
757f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_ALTERNATIVE)
758f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.TIMES_CONTACTED)
759f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.LAST_TIME_CONTACTED)
760f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CUSTOM_RINGTONE)
761f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SEND_TO_VOICEMAIL)
762f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
763f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.AGGREGATION_MODE)
76424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
765f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
766f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
767f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
768f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
769a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the raw entity view*/
770f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawEntityProjectionMap = ProjectionMap.builder()
771f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
772f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
773f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.Entity.DATA_ID)
774f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
775f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
77624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
777f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
778f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
779f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
780f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
781f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
782a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the contact entity view*/
783f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sEntityProjectionMap = ProjectionMap.builder()
784f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity._ID)
785f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.CONTACT_ID)
786f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.RAW_CONTACT_ID)
787f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DATA_ID)
788f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.NAME_RAW_CONTACT_ID)
789f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DELETED)
79024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(Contacts.IS_USER_PROFILE)
791f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
792f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
793f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
794f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
795f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
796f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
797f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
798f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
79958795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    /** Contains columns in PhoneLookup which are not contained in the data view. */
80058795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    private static final ProjectionMap sSipLookupColumns = ProjectionMap.builder()
80158795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .add(PhoneLookup.NUMBER, SipAddress.SIP_ADDRESS)
80258795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .add(PhoneLookup.TYPE, "0")
80358795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .add(PhoneLookup.LABEL, "NULL")
80458795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .add(PhoneLookup.NORMALIZED_NUMBER, "NULL")
80558795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .build();
80658795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda
8074a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /** Contains columns from the data view */
808f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataProjectionMap = ProjectionMap.builder()
809f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID)
810f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RAW_CONTACT_ID)
811f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CONTACT_ID)
812f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.NAME_RAW_CONTACT_ID)
81324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
814f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
815f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
816f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
817f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
818f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
819f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
820f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
82158795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    /** Contains columns from the data view used for SIP address lookup. */
82258795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    private static final ProjectionMap sDataSipLookupProjectionMap = ProjectionMap.builder()
82358795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .addAll(sDataProjectionMap)
82458795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .addAll(sSipLookupColumns)
82558795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .build();
82658795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda
8275e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /** Contains columns from the data view */
828f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDistinctDataProjectionMap = ProjectionMap.builder()
829f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID, "MIN(" + Data._ID + ")")
830f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
83124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
832f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
833f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
834f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
835f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
836f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
837f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
83858795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    /** Contains columns from the data view used for SIP address lookup. */
83958795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    private static final ProjectionMap sDistinctDataSipLookupProjectionMap = ProjectionMap.builder()
84058795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .addAll(sDistinctDataProjectionMap)
84158795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .addAll(sSipLookupColumns)
84258795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .build();
84358795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda
8449261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
845f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sPhoneLookupProjectionMap = ProjectionMap.builder()
846f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup._ID, "contacts_view." + Contacts._ID)
847f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LOOKUP_KEY, "contacts_view." + Contacts.LOOKUP_KEY)
848f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.DISPLAY_NAME, "contacts_view." + Contacts.DISPLAY_NAME)
849f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LAST_TIME_CONTACTED, "contacts_view." + Contacts.LAST_TIME_CONTACTED)
850f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TIMES_CONTACTED, "contacts_view." + Contacts.TIMES_CONTACTED)
851f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.STARRED, "contacts_view." + Contacts.STARRED)
852f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.IN_VISIBLE_GROUP, "contacts_view." + Contacts.IN_VISIBLE_GROUP)
853f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.PHOTO_ID, "contacts_view." + Contacts.PHOTO_ID)
8543d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_URI, "contacts_view." + Contacts.PHOTO_URI)
8553d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_THUMBNAIL_URI, "contacts_view." + Contacts.PHOTO_THUMBNAIL_URI)
856f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.CUSTOM_RINGTONE, "contacts_view." + Contacts.CUSTOM_RINGTONE)
857f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.HAS_PHONE_NUMBER, "contacts_view." + Contacts.HAS_PHONE_NUMBER)
858f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.SEND_TO_VOICEMAIL, "contacts_view." + Contacts.SEND_TO_VOICEMAIL)
859f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.NUMBER, Phone.NUMBER)
860f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TYPE, Phone.TYPE)
861f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LABEL, Phone.LABEL)
8622530512f639c4979fd7371c7dd25dd67e8118124Bai Tao            .add(PhoneLookup.NORMALIZED_NUMBER, Phone.NORMALIZED_NUMBER)
863f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
864f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
865ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
866f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsProjectionMap = ProjectionMap.builder()
867f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups._ID)
868f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_NAME)
869f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_TYPE)
87043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(Groups.DATA_SET)
87143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(Groups.ACCOUNT_TYPE_AND_DATA_SET)
872f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SOURCE_ID)
873f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DIRTY)
874f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.VERSION)
875f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.RES_PACKAGE)
876f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE)
877f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE_RES)
878f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.GROUP_VISIBLE)
879f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYSTEM_ID)
880f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DELETED)
881f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.NOTES)
882f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SHOULD_SYNC)
883f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.FAVORITES)
884f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.AUTO_ADD)
885c039cfb78c40730483fd71178df63ada5826a315Dmitri Plotnikov            .add(Groups.GROUP_IS_READ_ONLY)
886f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC1)
887f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC2)
888f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC3)
889f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC4)
890f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
891f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
89223ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki    /**
89323ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki     * Contains {@link Groups} columns along with summary details.
89423ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki     *
89523ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki     * Note {@link Groups#SUMMARY_COUNT} doesn't exist in groups/view_groups.
89623ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki     * When we detect this column being requested, we join {@link Joins#GROUP_MEMBER_COUNT} to
89723ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki     * generate it.
89823ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki     */
899f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsSummaryProjectionMap = ProjectionMap.builder()
900f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sGroupsProjectionMap)
90123ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki            .add(Groups.SUMMARY_COUNT, "ifnull(group_member_count, 0)")
902f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_WITH_PHONES,
903f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                    "(SELECT COUNT(" + ContactsColumns.CONCRETE_ID + ") FROM "
904f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + Tables.CONTACTS_JOIN_RAW_CONTACTS_DATA_FILTERED_BY_GROUPMEMBERSHIP
905f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + " WHERE " + Contacts.HAS_PHONE_NUMBER + ")")
906f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            .build();
907f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa
908f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa    // This is only exposed as hidden API for the contacts app, so we can be very specific in
909f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa    // the filtering
910f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa    private static final ProjectionMap sGroupsSummaryProjectionMapWithGroupCountPerAccount =
911f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            ProjectionMap.builder()
912f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            .addAll(sGroupsSummaryProjectionMap)
913f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            .add(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT,
914f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                    "(SELECT COUNT(*) FROM " + Views.GROUPS + " WHERE "
915f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + "(" + Groups.ACCOUNT_NAME + "="
916f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + GroupsColumns.CONCRETE_ACCOUNT_NAME
917f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + " AND "
918f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.ACCOUNT_TYPE + "=" + GroupsColumns.CONCRETE_ACCOUNT_TYPE
919f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + " AND "
920f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.DELETED + "=0 AND "
921f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.FAVORITES + "=0 AND "
922f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.AUTO_ADD + "=0"
923f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + ")"
924f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + " GROUP BY "
925f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.ACCOUNT_NAME + ", " + Groups.ACCOUNT_TYPE
926f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                   + ")")
927f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
928f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
929373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains the agg_exceptions columns */
930f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sAggregationExceptionsProjectionMap = ProjectionMap.builder()
931f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id")
932f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.TYPE)
933f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID1)
934f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID2)
935f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
936f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
937eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    /** Contains the agg_exceptions columns */
938f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSettingsProjectionMap = ProjectionMap.builder()
939f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_NAME)
940f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_TYPE)
941f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            .add(Settings.DATA_SET)
942f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_VISIBLE)
943f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.SHOULD_SYNC)
944f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ANY_UNSYNCED,
945f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN MIN(" + Settings.SHOULD_SYNC
946f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                        + ",(SELECT "
947f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + "(CASE WHEN MIN(" + Groups.SHOULD_SYNC + ") IS NULL"
948f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " THEN 1"
949f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " ELSE MIN(" + Groups.SHOULD_SYNC + ")"
950f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " END)"
951f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.GROUPS
952f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
953f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_NAME
954f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
955f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                                    + SettingsColumns.CONCRETE_ACCOUNT_TYPE
956f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                                + " AND ((" + GroupsColumns.CONCRETE_DATA_SET + " IS NULL AND "
957f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                                    + SettingsColumns.CONCRETE_DATA_SET + " IS NULL) OR ("
958f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                                    + GroupsColumns.CONCRETE_DATA_SET + "="
959f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                                    + SettingsColumns.CONCRETE_DATA_SET + "))))=0"
960f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN 1"
961f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE 0"
962f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " END)")
963f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_COUNT,
964f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
965f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
966f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
967f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
968f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
969f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
970f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_WITH_PHONES,
971f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
972f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
973f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
974f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + Contacts.HAS_PHONE_NUMBER
975f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
976f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
977f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
978f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
979f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
98082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Contains StatusUpdates columns */
981f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStatusUpdatesProjectionMap = ProjectionMap.builder()
982f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PresenceColumns.RAW_CONTACT_ID)
983f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.DATA_ID, DataColumns.CONCRETE_ID)
984f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_ACCOUNT)
985f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_HANDLE)
986f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PROTOCOL)
987f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // We cannot allow a null in the custom protocol field, because SQLite3 does not
988f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // properly enforce uniqueness of null values
989f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CUSTOM_PROTOCOL,
990f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN " + StatusUpdates.CUSTOM_PROTOCOL + "=''"
991f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN NULL"
992f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE " + StatusUpdates.CUSTOM_PROTOCOL + " END)")
993f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PRESENCE)
994f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CHAT_CAPABILITY)
995f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS)
996f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_TIMESTAMP)
997f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_RES_PACKAGE)
998f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_ICON)
999f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_LABEL)
1000f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
1001f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
10023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /** Contains StreamItems columns */
10033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final ProjectionMap sStreamItemsProjectionMap = ProjectionMap.builder()
10049b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems._ID)
10059b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.CONTACT_ID)
1006af10329f85c5d8c4196c495a9f0f9a6c6ecbc231Daniel Lehmann            .add(StreamItems.CONTACT_LOOKUP_KEY)
10079b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.ACCOUNT_NAME)
10089b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.ACCOUNT_TYPE)
10099b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.DATA_SET)
10103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RAW_CONTACT_ID)
10119b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.RAW_CONTACT_SOURCE_ID)
10123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_PACKAGE)
10133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_ICON)
10143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_LABEL)
10153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.TEXT)
10163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.TIMESTAMP)
10173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.COMMENTS)
10180bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.SYNC1)
10190bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.SYNC2)
10200bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.SYNC3)
10210bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.SYNC4)
10223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .build();
10233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
10243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final ProjectionMap sStreamItemPhotosProjectionMap = ProjectionMap.builder()
10253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos._ID, StreamItemPhotosColumns.CONCRETE_ID)
10263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RAW_CONTACT_ID)
10270bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.RAW_CONTACT_SOURCE_ID, RawContactsColumns.CONCRETE_SOURCE_ID)
10283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.STREAM_ITEM_ID)
10293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.SORT_INDEX)
10306802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            .add(StreamItemPhotos.PHOTO_FILE_ID)
10316802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            .add(StreamItemPhotos.PHOTO_URI,
10326802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    "'" + DisplayPhoto.CONTENT_URI + "'||'/'||" + StreamItemPhotos.PHOTO_FILE_ID)
10331dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.HEIGHT)
10341dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.WIDTH)
10351dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.FILESIZE)
10360bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItemPhotos.SYNC1)
10370bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItemPhotos.SYNC2)
10380bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItemPhotos.SYNC3)
10390bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItemPhotos.SYNC4)
10403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .build();
10413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
1042d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /** Contains {@link Directory} columns */
1043f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDirectoryProjectionMap = ProjectionMap.builder()
1044f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory._ID)
1045f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.PACKAGE_NAME)
1046f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.TYPE_RESOURCE_ID)
1047f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DISPLAY_NAME)
1048f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DIRECTORY_AUTHORITY)
1049f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_TYPE)
1050f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_NAME)
1051f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.EXPORT_SUPPORT)
1052778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.SHORTCUT_SUPPORT)
1053778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.PHOTO_SUPPORT)
1054f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
10557e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
10569705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    // where clause to update the status_updates table
10579705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private static final String WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE =
10589705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdatesColumns.DATA_ID + " IN (SELECT Distinct " + StatusUpdates.DATA_ID +
10599705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " FROM " + Tables.STATUS_UPDATES + " LEFT OUTER JOIN " + Tables.PRESENCE +
10609705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " ON " + StatusUpdatesColumns.DATA_ID + " = " + StatusUpdates.DATA_ID + " WHERE ";
10619705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
10622526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private static final String[] EMPTY_STRING_ARRAY = new String[0];
10632526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
1064bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1065bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Notification ID for failure to import contacts.
1066bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1067bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private static final int LEGACY_IMPORT_FAILED_NOTIFICATION = 1;
106851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
106903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_START_MATCH = "[";
107003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_END_MATCH = "]";
107103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_ELLIPSIS = "...";
107203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final int DEFAULT_SNIPPET_ARG_MAX_TOKENS = -10;
107303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
10749a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    private boolean sIsPhoneInitialized;
10759a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    private boolean sIsPhone;
10769a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
1077f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private StringBuilder mSb = new StringBuilder();
10781129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs1 = new String[1];
10791129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs2 = new String[2];
10802526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private ArrayList<String> mSelectionArgs = Lists.newArrayList();
10812526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
1082f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private Account mAccount;
1083f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
108446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
108546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Stores mapping from type Strings exposed via {@link DataUsageFeedback} to
108646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * type integers in {@link DataUsageStatColumns}.
108746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
108846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final Map<String, Integer> sDataUsageTypeMap;
108946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
10904f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
10914f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
1092a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
1093d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
1094d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
1095a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_ID_DATA);
1096a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/entities", CONTACTS_ID_ENTITIES);
10973653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
10983653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
10992d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*",
11002d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                AGGREGATION_SUGGESTIONS);
1101a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_ID_PHOTO);
1102f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/display_photo",
1103f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_ID_DISPLAY_PHOTO);
11043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/stream_items",
11053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_ID_STREAM_ITEMS);
1106c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter", CONTACTS_FILTER);
11075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER);
11085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP);
11092149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/data", CONTACTS_LOOKUP_DATA);
1110bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/photo",
1111bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                CONTACTS_LOOKUP_PHOTO);
11125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
11132149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/data",
11142149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                CONTACTS_LOOKUP_ID_DATA);
1115bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/photo",
1116bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                CONTACTS_LOOKUP_ID_PHOTO);
1117f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/display_photo",
1118f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_LOOKUP_DISPLAY_PHOTO);
1119f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/display_photo",
1120f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_LOOKUP_ID_DISPLAY_PHOTO);
1121a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/entities",
1122a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ENTITIES);
1123a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/entities",
1124a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ID_ENTITIES);
11253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/stream_items",
11263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_LOOKUP_STREAM_ITEMS);
11273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/stream_items",
11283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_LOOKUP_ID_STREAM_ITEMS);
1129f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_vcard/*", CONTACTS_AS_VCARD);
113042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_multi_vcard/*",
113142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                CONTACTS_AS_MULTI_VCARD);
11325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT);
1133ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*",
1134ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                CONTACTS_STREQUENT_FILTER);
11355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP);
113645ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa        matcher.addURI(ContactsContract.AUTHORITY, "contacts/frequent", CONTACTS_FREQUENT);
11373653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
11385ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
11395ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
11405ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA);
1141f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/display_photo",
1142f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                RAW_CONTACTS_ID_DISPLAY_PHOTO);
114346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ENTITY_ID);
11443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/stream_items",
11453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                RAW_CONTACTS_ID_STREAM_ITEMS);
114682780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/stream_items/#",
114782780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                RAW_CONTACTS_ID_STREAM_ITEMS_ID);
114846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
114946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contact_entities", RAW_CONTACT_ENTITIES);
1150b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
11514f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
11524f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
1153ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
115448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID);
11555e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER);
1156ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
11574a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS);
115848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID);
11591dac83b8fa58944acfd00f44e717a7dddc659d2dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup", EMAILS_LOOKUP);
11605e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP);
11615e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER);
11624a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
1163ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
116448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
116546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        /** "*" is in CSV form with data ids ("123,456,789") */
116646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        matcher.addURI(ContactsContract.AUTHORITY, "data/usagefeedback/*", DATA_USAGE_FEEDBACK_ID);
11671f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1168ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
1169ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
1170ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
1171ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
117235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
1173b5a4add17815167d20a90645779df34cdf45280dFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#",
1174b5a4add17815167d20a90645779df34cdf45280dFred Quintana                SYNCSTATE_ID);
11755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/" + SyncStateContentProviderHelper.PATH,
11765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                PROFILE_SYNCSTATE);
11775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY,
11785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                "profile/" + SyncStateContentProviderHelper.PATH + "/#",
11795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                PROFILE_SYNCSTATE_ID);
118035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1181a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
1182b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
1183b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
1184b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
1185b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
11864f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1187eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS);
1188eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
118982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates", STATUS_UPDATES);
119082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates/#", STATUS_UPDATES_ID);
11911f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1192c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
1193c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
1194c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
1195c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
11962d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*",
1197c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SHORTCUT);
1198c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
119909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "provider_status", PROVIDER_STATUS);
1200d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
1201d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories", DIRECTORIES);
1202d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories/#", DIRECTORIES_ID);
12037a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
12047a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "complete_name", COMPLETE_NAME);
120524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
120624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile", PROFILE);
120724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/entities", PROFILE_ENTITIES);
120824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/data", PROFILE_DATA);
120924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/data/#", PROFILE_DATA_ID);
121085077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/photo", PROFILE_PHOTO);
121185077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/display_photo", PROFILE_DISPLAY_PHOTO);
121224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/as_vcard", PROFILE_AS_VCARD);
121324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts", PROFILE_RAW_CONTACTS);
121424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#",
121524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID);
121624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/data",
121724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID_DATA);
121824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/entity",
121924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID_ENTITIES);
12205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/status_updates",
12215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                PROFILE_STATUS_UPDATES);
12223202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contact_entities",
12233202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro                PROFILE_RAW_CONTACT_ENTITIES);
122446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
12253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items", STREAM_ITEMS);
12263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/photo", STREAM_ITEMS_PHOTOS);
12273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#", STREAM_ITEMS_ID);
12283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#/photo", STREAM_ITEMS_ID_PHOTOS);
12293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#/photo/#",
12303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                STREAM_ITEMS_ID_PHOTOS_ID);
12313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items_limit", STREAM_ITEMS_LIMIT);
12323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
12335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "display_photo/#", DISPLAY_PHOTO);
1234f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "photo_dimensions", PHOTO_DIMENSIONS);
1235f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
123646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        HashMap<String, Integer> tmpTypeMap = new HashMap<String, Integer>();
123746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_CALL, DataUsageStatColumns.USAGE_TYPE_INT_CALL);
123846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_LONG_TEXT,
123946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT);
124046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_SHORT_TEXT,
124146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DataUsageStatColumns.USAGE_TYPE_INT_SHORT_TEXT);
124246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        sDataUsageTypeMap = Collections.unmodifiableMap(tmpTypeMap);
124319a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    }
124419a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
1245d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static class DirectoryInfo {
1246d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String authority;
1247d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountName;
1248d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountType;
1249d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
1250d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
1251d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
1252d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Cached information about contact directories.
1253d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
12544458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private HashMap<String, DirectoryInfo> mDirectoryCache = new HashMap<String, DirectoryInfo>();
12554458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private boolean mDirectoryCacheValid = false;
1256d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
12573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
125843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * An entry in group id cache. It maps the combination of (account type, account name, data set,
1259ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * and source id) to group row id.
1260ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     */
1261e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    public static class GroupIdCacheEntry {
1262ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType;
1263ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName;
126443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String dataSet;
1265ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String sourceId;
1266ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        long groupId;
1267ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    }
1268a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1269e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // We don't need a soft cache for groups - the assumption is that there will only
1270e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // be a small number of contact groups. The cache is keyed off source id.  The value
1271e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // is a list of groups with this group id.
1272e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    private HashMap<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache = Maps.newHashMap();
1273e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov
127424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
1275f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Maximum dimension (height or width) of display photos.  Larger images will be scaled
1276f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * to fit.
1277f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
1278f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private int mMaxDisplayPhotoDim;
1279f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1280f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
1281f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Maximum dimension (height or width) of photo thumbnails.
1282f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
1283f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private int mMaxThumbnailPhotoDim;
1284f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
12855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
12865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Sub-provider for handling profile requests against the profile database.
12875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
12885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private ProfileProvider mProfileProvider;
1289f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
12904097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
1291f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private NameLookupBuilder mNameLookupBuilder;
1292315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
1293622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private PostalSplitter mPostalSplitter;
1294622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
129572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    private ContactDirectoryManager mContactDirectoryManager;
12965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
1297078f588cef389358adabc579de00747878f3c108Dave Santoro    // The database tag to use for representing the contacts DB in contacts transactions.
1298078f588cef389358adabc579de00747878f3c108Dave Santoro    /* package */ static final String CONTACTS_DB_TAG = "contacts";
1299078f588cef389358adabc579de00747878f3c108Dave Santoro
1300078f588cef389358adabc579de00747878f3c108Dave Santoro    // The database tag to use for representing the profile DB in contacts transactions.
1301078f588cef389358adabc579de00747878f3c108Dave Santoro    /* package */ static final String PROFILE_DB_TAG = "profile";
1302078f588cef389358adabc579de00747878f3c108Dave Santoro
13035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
13045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * The active (thread-local) database.  This will be switched between a contacts-specific
13055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * database and a profile-specific database, depending on what the current operation is
13065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * targeted to.
13075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
13085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<SQLiteDatabase> mActiveDb = new ThreadLocal<SQLiteDatabase>();
13095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
13106efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    /**
13116efb7db26598b105342d02207e0ca1c8725c10daDave Santoro     * The thread-local holder of the active transaction.  Shared between this and the profile
13126efb7db26598b105342d02207e0ca1c8725c10daDave Santoro     * provider, to keep transactions on both databases synchronized.
13136efb7db26598b105342d02207e0ca1c8725c10daDave Santoro     */
13146efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    private final ThreadLocal<ContactsTransaction> mTransactionHolder =
13156efb7db26598b105342d02207e0ca1c8725c10daDave Santoro            new ThreadLocal<ContactsTransaction>();
13166efb7db26598b105342d02207e0ca1c8725c10daDave Santoro
13175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // This variable keeps track of whether the current operation is intended for the profile DB.
13185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<Boolean> mInProfileMode = new ThreadLocal<Boolean>();
13195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
13205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Separate data row handler instances for contact data and profile data.
13215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private HashMap<String, DataRowHandler> mDataRowHandlers;
13225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private HashMap<String, DataRowHandler> mProfileDataRowHandlers;
13235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
13245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Depending on whether the action being performed is for the profile, we will use one of two
13255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // database helper instances.
13265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<ContactsDatabaseHelper> mDbHelper =
13275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            new ThreadLocal<ContactsDatabaseHelper>();
13285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private ContactsDatabaseHelper mContactsHelper;
13295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private ProfileDatabaseHelper mProfileHelper;
13305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
13315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Depending on whether the action being performed is for the profile or not, we will use one of
13325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // two aggregator instances.
13335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<ContactAggregator> mAggregator = new ThreadLocal<ContactAggregator>();
1334622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private ContactAggregator mContactAggregator;
13355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private ContactAggregator mProfileAggregator;
13365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
13375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Depending on whether the action being performed is for the profile or not, we will use one of
13385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // two photo store instances (with their files stored in separate subdirectories).
13395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<PhotoStore> mPhotoStore = new ThreadLocal<PhotoStore>();
13405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private PhotoStore mContactsPhotoStore;
13415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private PhotoStore mProfilePhotoStore;
13425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
13435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // The active transaction context will switch depending on the operation being performed.
13445d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Both transaction contexts will be cleared out when a batch transaction is started, and
13455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // each will be processed separately when a batch transaction completes.
13465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private TransactionContext mContactTransactionContext = new TransactionContext(false);
13475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private TransactionContext mProfileTransactionContext = new TransactionContext(true);
13485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<TransactionContext> mTransactionContext =
13495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            new ThreadLocal<TransactionContext>();
13505d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
135182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    // Duration in milliseconds that pre-authorized URIs will remain valid.
135282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    private long mPreAuthorizedUriDuration;
135382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
135482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    // Map of single-use pre-authorized URIs to expiration times.
135582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    private Map<Uri, Long> mPreAuthorizedUris = Maps.newHashMap();
135682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
1357d0eb93009559d095de0448907527aeb059801dc4Dave Santoro    // Random number generator.
1358d0eb93009559d095de0448907527aeb059801dc4Dave Santoro    private SecureRandom mRandom = new SecureRandom();
135982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
1360f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
1361a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    private GlobalSearchSupport mGlobalSearchSupport;
1362d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov    private CommonNicknameCache mCommonNicknameCache;
1363f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    private SearchIndexManager mSearchIndexManager;
1364a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
136520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private ContentValues mValues = new ContentValues();
136673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    private HashMap<String, Boolean> mAccountWritability = Maps.newHashMap();
136720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
136809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private int mProviderStatus = ProviderStatus.STATUS_NORMAL;
13693826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private boolean mProviderStatusUpdateNeeded;
137009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private long mEstimatedStorageRequirement = 0;
137115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private volatile CountDownLatch mReadAccessLatch;
137215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private volatile CountDownLatch mWriteAccessLatch;
137315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private boolean mAccountUpdateListenerRegistered;
1374bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private boolean mOkToOpenAccess = true;
137573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
13761a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey    private boolean mVisibleTouched = false;
13771a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
137881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    private boolean mSyncToNetwork;
137981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
13804cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    private Locale mCurrentLocale;
13813826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private int mContactsAccountCount;
1382d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov
1383bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private HandlerThread mBackgroundThread;
1384bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private Handler mBackgroundHandler;
1385bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1386f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private long mLastPhotoCleanup = 0;
1387f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
13884f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
13894f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
1390663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki        if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
1391663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki            Log.d(Constants.PERFORMANCE_TAG, "ContactsProvider2.onCreate start");
1392663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki        }
1393de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        super.onCreate();
1394ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        try {
1395ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return initialize();
1396ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        } catch (RuntimeException e) {
1397ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            Log.e(TAG, "Cannot start provider", e);
1398ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return false;
1399663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki        } finally {
1400663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki            if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
1401663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki                Log.d(Constants.PERFORMANCE_TAG, "ContactsProvider2.onCreate finish");
1402663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki            }
1403ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        }
1404ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    }
140535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1406ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    private boolean initialize() {
140715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        StrictMode.setThreadPolicy(
140815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
140915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
14103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Resources resources = getContext().getResources();
1411f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mMaxDisplayPhotoDim = resources.getInteger(
1412f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                R.integer.config_max_display_photo_dim);
1413f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mMaxThumbnailPhotoDim = resources.getInteger(
1414f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                R.integer.config_max_thumbnail_photo_dim);
14153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
1416078f588cef389358adabc579de00747878f3c108Dave Santoro        mContactsHelper = getDatabaseHelper(getContext());
14175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mDbHelper.set(mContactsHelper);
1418078f588cef389358adabc579de00747878f3c108Dave Santoro
1419078f588cef389358adabc579de00747878f3c108Dave Santoro        // Set up the DB helper for keeping transactions serialized.
1420078f588cef389358adabc579de00747878f3c108Dave Santoro        setDbHelperToSerializeOn(mContactsHelper, CONTACTS_DB_TAG);
1421078f588cef389358adabc579de00747878f3c108Dave Santoro
142272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        mContactDirectoryManager = new ContactDirectoryManager(this);
1423a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mGlobalSearchSupport = new GlobalSearchSupport(this);
142465ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov
1425bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        // The provider is closed for business until fully initialized
142615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mReadAccessLatch = new CountDownLatch(1);
142715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mWriteAccessLatch = new CountDownLatch(1);
142872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
1429bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundThread = new HandlerThread("ContactsProviderWorker",
1430bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                Process.THREAD_PRIORITY_BACKGROUND);
1431bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundThread.start();
1432bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler = new Handler(mBackgroundThread.getLooper()) {
1433bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            @Override
1434bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            public void handleMessage(Message msg) {
1435bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                performBackgroundTask(msg.what, msg.obj);
1436bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1437bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        };
14382a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov
14395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Set up the sub-provider for handling profiles.
14405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileProvider = getProfileProvider();
1441c990980ab4beb7b81c3337526f1bdcd5d1a14730Dave Santoro        mProfileProvider.setDbHelperToSerializeOn(mContactsHelper, CONTACTS_DB_TAG);
14425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        ProviderInfo profileInfo = new ProviderInfo();
14435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        profileInfo.readPermission = "android.permission.READ_PROFILE";
14445d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        profileInfo.writePermission = "android.permission.WRITE_PROFILE";
14455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileProvider.attachInfo(getContext(), profileInfo);
1446078f588cef389358adabc579de00747878f3c108Dave Santoro        mProfileHelper = mProfileProvider.getDatabaseHelper(getContext());
14475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
144882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        // Initialize the pre-authorized URI duration.
144982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        mPreAuthorizedUriDuration = android.provider.Settings.Secure.getLong(
145082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                getContext().getContentResolver(),
145182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                android.provider.Settings.Secure.CONTACTS_PREAUTH_URI_EXPIRATION,
145282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                DEFAULT_PREAUTHORIZED_URI_EXPIRATION);
145382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
145415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_INITIALIZE);
1455bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS);
1456bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
1457bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_LOCALE);
1458bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM);
145905e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_SEARCH_INDEX);
1460bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_PROVIDER_STATUS);
146115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_OPEN_WRITE_ACCESS);
1462f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
14633826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
146449d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov        return true;
14654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
14664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1467767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    /**
146851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * (Re)allocates all locale-sensitive structures.
146951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
147004b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov    private void initForDefaultLocale() {
147115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        Context context = getContext();
14725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mLegacyApiSupport = new LegacyApiSupport(context, mContactsHelper, this,
14735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mGlobalSearchSupport);
14744cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mCurrentLocale = getLocale();
14755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mNameSplitter = mContactsHelper.createNameSplitter();
14764cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
14774cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mPostalSplitter = new PostalSplitter(mCurrentLocale);
14785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mCommonNicknameCache = new CommonNicknameCache(mContactsHelper.getReadableDatabase());
1479cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao        ContactLocaleUtils.getIntance().setLocale(mCurrentLocale);
14805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactAggregator = new ContactAggregator(this, mContactsHelper,
148115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                createPhotoPriorityResolver(context), mNameSplitter, mCommonNicknameCache);
14825b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
14835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileAggregator = new ProfileAggregator(this, mProfileHelper,
14845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                createPhotoPriorityResolver(context), mNameSplitter, mCommonNicknameCache);
14855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
1486f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov        mSearchIndexManager = new SearchIndexManager(this);
14875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
14885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsPhotoStore = new PhotoStore(getContext().getFilesDir(), mContactsHelper);
14895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfilePhotoStore = new PhotoStore(new File(getContext().getFilesDir(), "profile"),
14905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mProfileHelper);
14915b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
1492bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers = new HashMap<String, DataRowHandler>();
14935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        initDataRowHandlers(mDataRowHandlers, mContactsHelper, mContactAggregator,
14945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mContactsPhotoStore);
14955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileDataRowHandlers = new HashMap<String, DataRowHandler>();
14965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        initDataRowHandlers(mProfileDataRowHandlers, mProfileHelper, mProfileAggregator,
14975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mProfilePhotoStore);
14985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
14995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Set initial thread-local state variables for the Contacts DB.
15005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        switchToContactMode();
15015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
1502bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
15035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private void initDataRowHandlers(Map<String, DataRowHandler> handlerMap,
15045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            ContactsDatabaseHelper dbHelper, ContactAggregator contactAggregator,
15055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            PhotoStore photoStore) {
15065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Context context = getContext();
15075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Email.CONTENT_ITEM_TYPE,
15085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForEmail(context, dbHelper, contactAggregator));
15095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Im.CONTENT_ITEM_TYPE,
15105d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForIm(context, dbHelper, contactAggregator));
15115d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Organization.CONTENT_ITEM_TYPE,
15125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForOrganization(context, dbHelper, contactAggregator));
15135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Phone.CONTENT_ITEM_TYPE,
15145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForPhoneNumber(context, dbHelper, contactAggregator));
15155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Nickname.CONTENT_ITEM_TYPE,
15165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForNickname(context, dbHelper, contactAggregator));
15175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(StructuredName.CONTENT_ITEM_TYPE,
15185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForStructuredName(context, dbHelper, contactAggregator,
1519bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mNameSplitter, mNameLookupBuilder));
15205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(StructuredPostal.CONTENT_ITEM_TYPE,
15215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForStructuredPostal(context, dbHelper, contactAggregator,
1522bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mPostalSplitter));
15235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(GroupMembership.CONTENT_ITEM_TYPE,
15245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForGroupMembership(context, dbHelper, contactAggregator,
1525bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mGroupIdCache));
15265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Photo.CONTENT_ITEM_TYPE,
15275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForPhoto(context, dbHelper, contactAggregator, photoStore));
15285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Note.CONTENT_ITEM_TYPE,
15295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForNote(context, dbHelper, contactAggregator));
1530bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1531bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1532bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    /**
1533bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     * Visible for testing.
1534bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     */
1535bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    /* package */ PhotoPriorityResolver createPhotoPriorityResolver(Context context) {
1536bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        return new PhotoPriorityResolver(context);
1537bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1538bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1539bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task) {
1540bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendEmptyMessage(task);
1541bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1542bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1543bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task, Object arg) {
1544bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendMessage(mBackgroundHandler.obtainMessage(task, arg));
1545bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1546bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1547bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void performBackgroundTask(int task, Object arg) {
1548bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        switch (task) {
154915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_INITIALIZE: {
155015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                initForDefaultLocale();
155115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch.countDown();
155215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch = null;
155315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                break;
155415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            }
155515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
155615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_OPEN_WRITE_ACCESS: {
1557bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (mOkToOpenAccess) {
155815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch.countDown();
155915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch = null;
1560bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1561bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1562bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1563bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1564bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS: {
1565bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (isLegacyContactImportNeeded()) {
1566bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    importLegacyContactsInBackground();
1567bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1568bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1569bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1570bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1571bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_ACCOUNTS: {
157215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Context context = getContext();
157315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                if (!mAccountUpdateListenerRegistered) {
157415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    AccountManager.get(context).addOnAccountsUpdatedListener(this, null, false);
157515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mAccountUpdateListenerRegistered = true;
157615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                }
157715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
15785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                // Update the accounts for both the contacts and profile DBs.
157915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Account[] accounts = AccountManager.get(context).getAccounts();
15805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                switchToContactMode();
1581bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                boolean accountsChanged = updateAccountsInBackground(accounts);
15825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                switchToProfileMode();
15835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                accountsChanged |= updateAccountsInBackground(accounts);
15845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
1585bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateContactsAccountCount(accounts);
1586bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateDirectoriesInBackground(accountsChanged);
1587bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1588bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1589bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1590bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_LOCALE: {
1591bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateLocaleInBackground();
1592bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1593bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1594bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1595fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            case BACKGROUND_TASK_CHANGE_LOCALE: {
1596fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                changeLocaleInBackground();
1597fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                break;
1598fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            }
1599fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1600bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM: {
1601bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (isAggregationUpgradeNeeded()) {
1602bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    upgradeAggregationAlgorithmInBackground();
1603bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1604bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1605bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1606bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
160705e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_SEARCH_INDEX: {
160805e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                updateSearchIndexInBackground();
160905e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                break;
161005e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            }
161105e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
1612bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_PROVIDER_STATUS: {
1613bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateProviderStatus();
1614bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1615bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1616bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1617bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_DIRECTORIES: {
1618bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (arg != null) {
1619bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    mContactDirectoryManager.onPackageChanged((String) arg);
1620bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1621bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1622bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1623f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1624f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case BACKGROUND_TASK_CLEANUP_PHOTOS: {
1625f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Check rate limit.
1626f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long now = System.currentTimeMillis();
1627f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (now - mLastPhotoCleanup > PHOTO_CLEANUP_RATE_LIMIT) {
1628f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    mLastPhotoCleanup = now;
16295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
16305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    // Clean up photo stores for both contacts and profiles.
16315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    switchToContactMode();
16325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    cleanupPhotoStore();
16335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    switchToProfileMode();
1634f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    cleanupPhotoStore();
1635f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    break;
1636f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
1637f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1638bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
16394cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
16404cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao
164153fac8f99f3884c372c907a76766d27fa9e1d95fDmitri Plotnikov    public void onLocaleChanged() {
16423826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
16433826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
16444f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov            return;
16454f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov        }
16464f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov
1647fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_CHANGE_LOCALE);
16484cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
164951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
165051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    /**
165151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * Verifies that the contacts database is properly configured for the current locale.
165251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * If not, changes the database locale to the current locale using an asynchronous task.
165351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * This needs to be done asynchronously because the process involves rebuilding
165451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * large data structures (name lookup, sort keys), which can take minutes on
165551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * a large set of contacts.
165651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
1657bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateLocaleInBackground() {
1658f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
1659f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        // The process is already running - postpone the change
1660f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        if (mProviderStatus == ProviderStatus.STATUS_CHANGING_LOCALE) {
1661f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov            return;
1662f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        }
1663f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
166451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
166551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final String providerLocale = prefs.getString(PREF_LOCALE, null);
166651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final Locale currentLocale = mCurrentLocale;
166751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        if (currentLocale.toString().equals(providerLocale)) {
166851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            return;
166951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        }
167051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
167151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        int providerStatus = mProviderStatus;
167251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_CHANGING_LOCALE);
16735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsHelper.setLocale(this, currentLocale);
16745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileHelper.setLocale(this, currentLocale);
1675bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        prefs.edit().putString(PREF_LOCALE, currentLocale.toString()).apply();
1676bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        setProviderStatus(providerStatus);
1677bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
167851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
1679fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    /**
1680fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     * Reinitializes the provider for a new locale.
1681fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     */
1682fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    private void changeLocaleInBackground() {
1683fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Re-initializing the provider without stopping it.
1684fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Locking the database will prevent inserts/updates/deletes from
1685fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // running at the same time, but queries may still be running
1686fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // on other threads. Those queries may return inconsistent results.
16875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        SQLiteDatabase db = mContactsHelper.getWritableDatabase();
16885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        SQLiteDatabase profileDb = mProfileHelper.getWritableDatabase();
1689fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        db.beginTransaction();
16905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        profileDb.beginTransaction();
1691fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        try {
1692fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            initForDefaultLocale();
1693fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.setTransactionSuccessful();
16945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            profileDb.setTransactionSuccessful();
1695fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        } finally {
1696fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.endTransaction();
16975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            profileDb.endTransaction();
1698fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        }
1699fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1700fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        updateLocaleInBackground();
1701fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    }
1702fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
170305e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    protected void updateSearchIndexInBackground() {
170405e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        mSearchIndexManager.updateIndex();
170505e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    }
170605e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
1707bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateDirectoriesInBackground(boolean rescan) {
1708bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mContactDirectoryManager.scanAllPackages(rescan);
170951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
171051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
17113826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateProviderStatus() {
17123826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
17133826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
17143826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return;
17153826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
17163826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
17173e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson        // No accounts/no contacts status is true if there are no account and
17185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // there are no contacts or one profile contact
17193e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson        if (mContactsAccountCount == 0) {
17205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            long contactsNum = DatabaseUtils.queryNumEntries(mContactsHelper.getReadableDatabase(),
17213e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                    Tables.CONTACTS, null);
17225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            long profileNum = DatabaseUtils.queryNumEntries(mProfileHelper.getReadableDatabase(),
17235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Tables.CONTACTS, null);
17245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
17255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            // TODO: Different status if there is a profile but no contacts?
17265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (contactsNum == 0 && profileNum <= 1) {
17273e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                setProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS);
17283e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson            } else {
17293e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                setProviderStatus(ProviderStatus.STATUS_NORMAL);
17303e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson            }
17313826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } else {
17323826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            setProviderStatus(ProviderStatus.STATUS_NORMAL);
17333826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
17343826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
17353826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
173631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
1737f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    protected void cleanupPhotoStore() {
17385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
1739d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro        mActiveDb.set(db);
17406802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
17416802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Assemble the set of photo store file IDs that are in use, and send those to the photo
1742f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // store.  Any photos that aren't in that set will be deleted, and any photos that no
1743f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // longer exist in the photo store will be returned for us to clear out in the DB.
17447cf50494501938f175d288077145acf49da8f171Daniel Lehmann        long photoMimeTypeId = mDbHelper.get().getMimeTypeId(Photo.CONTENT_ITEM_TYPE);
17456802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Cursor c = db.query(Views.DATA, new String[]{Data._ID, Photo.PHOTO_FILE_ID},
17467cf50494501938f175d288077145acf49da8f171Daniel Lehmann                DataColumns.MIMETYPE_ID + "=" + photoMimeTypeId + " AND "
1747f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        + Photo.PHOTO_FILE_ID + " IS NOT NULL", null, null, null, null);
17486802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Set<Long> usedPhotoFileIds = Sets.newHashSet();
17496802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> photoFileIdToDataId = Maps.newHashMap();
1750f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        try {
1751f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            while (c.moveToNext()) {
17526802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long dataId = c.getLong(0);
17536802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long photoFileId = c.getLong(1);
17546802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                usedPhotoFileIds.add(photoFileId);
17556802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                photoFileIdToDataId.put(photoFileId, dataId);
17566802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
17576802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        } finally {
17586802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            c.close();
17596802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
17606802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
17616802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Also query for all social stream item photos.
1762c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro        c = db.query(Tables.STREAM_ITEM_PHOTOS + " JOIN " + Tables.STREAM_ITEMS
1763c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                + " ON " + StreamItemPhotos.STREAM_ITEM_ID + "=" + StreamItemsColumns.CONCRETE_ID
1764c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                + " JOIN " + Tables.RAW_CONTACTS
1765c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                + " ON " + StreamItems.RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID,
17666802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                new String[]{
1767c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                        StreamItemPhotosColumns.CONCRETE_ID,
1768c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                        StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID,
1769c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                        StreamItemPhotos.PHOTO_FILE_ID,
1770c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                        RawContacts.ACCOUNT_TYPE,
1771c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                        RawContacts.ACCOUNT_NAME
17726802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                },
17736802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                null, null, null, null, null);
17746802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> photoFileIdToStreamItemPhotoId = Maps.newHashMap();
17756802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> streamItemPhotoIdToStreamItemId = Maps.newHashMap();
1776c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro        Map<Long, Account> streamItemPhotoIdToAccount = Maps.newHashMap();
17776802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        try {
17786802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            while (c.moveToNext()) {
17796802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long streamItemPhotoId = c.getLong(0);
17806802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long streamItemId = c.getLong(1);
17816802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long photoFileId = c.getLong(2);
1782c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                String accountType = c.getString(3);
1783c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                String accountName = c.getString(4);
17846802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                usedPhotoFileIds.add(photoFileId);
17856802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                photoFileIdToStreamItemPhotoId.put(photoFileId, streamItemPhotoId);
17866802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                streamItemPhotoIdToStreamItemId.put(streamItemPhotoId, streamItemId);
1787c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                Account account = new Account(accountName, accountType);
1788c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                streamItemPhotoIdToAccount.put(photoFileId, account);
1789f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1790f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } finally {
1791f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            c.close();
1792f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
1793f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1794f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // Run the photo store cleanup.
17955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> missingPhotoIds = mPhotoStore.get().cleanup(usedPhotoFileIds);
1796f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1797d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro        // If any of the keys we're using no longer exist, clean them up.  We need to do these
1798d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro        // using internal APIs or direct DB access to avoid permission errors.
17996802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (!missingPhotoIds.isEmpty()) {
1800f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            try {
1801d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                db.beginTransactionWithListener(this);
1802d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                for (long missingPhotoId : missingPhotoIds) {
1803d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                    if (photoFileIdToDataId.containsKey(missingPhotoId)) {
1804d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        long dataId = photoFileIdToDataId.get(missingPhotoId);
1805d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        ContentValues updateValues = new ContentValues();
1806d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        updateValues.putNull(Photo.PHOTO_FILE_ID);
1807d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        updateData(ContentUris.withAppendedId(Data.CONTENT_URI, dataId),
1808d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                                updateValues, null, null, false);
1809d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                    }
1810d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                    if (photoFileIdToStreamItemPhotoId.containsKey(missingPhotoId)) {
1811d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        // For missing photos that were in stream item photos, just delete the
1812d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        // stream item photo.
1813d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        long streamItemPhotoId = photoFileIdToStreamItemPhotoId.get(missingPhotoId);
1814d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        db.delete(Tables.STREAM_ITEM_PHOTOS, StreamItemPhotos._ID + "=?",
1815d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                                new String[]{String.valueOf(streamItemPhotoId)});
1816d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                    }
1817d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                }
1818d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                db.setTransactionSuccessful();
1819d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro            } catch (Exception e) {
1820d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                // Cleanup failure is not a fatal problem.  We'll try again later.
1821d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                Log.e(TAG, "Failed to clean up outdated photo references", e);
1822d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro            } finally {
1823d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                db.endTransaction();
1824f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1825f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
1826f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
1827f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
18286efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    @Override
1829b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
1830b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        return ContactsDatabaseHelper.getInstance(context);
183131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
183231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
18336efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    @Override
18346efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    protected ThreadLocal<ContactsTransaction> getTransactionHolder() {
18356efb7db26598b105342d02207e0ca1c8725c10daDave Santoro        return mTransactionHolder;
18366efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    }
18376efb7db26598b105342d02207e0ca1c8725c10daDave Santoro
18385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    public ProfileProvider getProfileProvider() {
18395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return new ProfileProvider(this);
18405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
18415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
1842524913c66ce75ca8dec127ac88e3bc2249c246d9Dave Santoro    @VisibleForTesting
1843f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /* package */ PhotoStore getPhotoStore() {
18445d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mContactsPhotoStore;
1845f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
1846f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1847d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro    @VisibleForTesting
1848d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro    /* package */ PhotoStore getProfilePhotoStore() {
1849d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro        return mProfilePhotoStore;
1850d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro    }
1851d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro
185287614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    /* package */ int getMaxDisplayPhotoDim() {
185387614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro        return mMaxDisplayPhotoDim;
185487614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    }
185587614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro
185687614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    /* package */ int getMaxThumbnailPhotoDim() {
185787614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro        return mMaxThumbnailPhotoDim;
185887614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    }
185987614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro
1860013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    /* package */ NameSplitter getNameSplitter() {
1861013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        return mNameSplitter;
1862013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    }
1863013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov
18645df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    /* package */ NameLookupBuilder getNameLookupBuilder() {
18655df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov        return mNameLookupBuilder;
18665df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    }
18675df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov
18685dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    /* Visible for testing */
1869ed78fd6df5e9f3a2d572162e5d374d1f4a625bddDmitri Plotnikov    public ContactDirectoryManager getContactDirectoryManagerForTest() {
187072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        return mContactDirectoryManager;
187172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
187272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
187372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    /* Visible for testing */
18745dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    protected Locale getLocale() {
18755dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        return Locale.getDefault();
18765dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
18775dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
18785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private boolean inProfileMode() {
18795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Boolean profileMode = mInProfileMode.get();
18805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return profileMode != null && profileMode;
18815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
18825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
18833d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    protected boolean isLegacyContactImportNeeded() {
18845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int version = Integer.parseInt(
18855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mContactsHelper.getProperty(PROPERTY_CONTACTS_IMPORTED, "0"));
1886b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        return version < PROPERTY_CONTACTS_IMPORT_VERSION;
18873d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
18883d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1889568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected LegacyContactImporter getLegacyContactImporter() {
1890568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return new LegacyContactImporter(getContext(), this);
1891568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1892568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1893568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
1894bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     * Imports legacy contacts as a background task.
1895568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
1896bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private void importLegacyContactsInBackground() {
1897bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Importing legacy contacts");
1898bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADING);
1899568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1900bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
19015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsHelper.setLocale(this, mCurrentLocale);
1902bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        prefs.edit().putString(PREF_LOCALE, mCurrentLocale.toString()).commit();
1903568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1904bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        LegacyContactImporter importer = getLegacyContactImporter();
1905bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        if (importLegacyContacts(importer)) {
1906bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            onLegacyContactImportSuccess();
1907bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        } else {
1908bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            onLegacyContactImportFailure();
1909bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
1910568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1911568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1912bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1913bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Unlocks the provider and declares that the import process is complete.
1914bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1915bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportSuccess() {
1916bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
1917bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)getContext().getSystemService(Context.NOTIFICATION_SERVICE);
1918bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.cancel(LEGACY_IMPORT_FAILED_NOTIFICATION);
1919bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1920b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        // Store a property in the database indicating that the conversion process succeeded
19215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsHelper.setProperty(PROPERTY_CONTACTS_IMPORTED,
1922b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov                String.valueOf(PROPERTY_CONTACTS_IMPORT_VERSION));
1923bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_NORMAL);
1924bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Completed import of legacy contacts");
1925bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    }
1926bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1927bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1928bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Announces the provider status and keeps the provider locked.
1929bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1930bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportFailure() {
1931bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Context context = getContext();
1932bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
1933bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
1934bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1935bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        // Show a notification
1936bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Notification n = new Notification(android.R.drawable.stat_notify_error,
1937bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_ticker),
1938bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                System.currentTimeMillis());
1939bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.setLatestEventInfo(context,
1940bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_title),
1941bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_text),
1942bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                PendingIntent.getActivity(context, 0, new Intent(Intents.UI.LIST_DEFAULT), 0));
1943bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
1944bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1945bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.notify(LEGACY_IMPORT_FAILED_NOTIFICATION, n);
1946bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1947bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADE_OUT_OF_MEMORY);
1948bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Failed to import legacy contacts");
1949bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1950bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        // Do not let any database changes until this issue is resolved.
1951bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mOkToOpenAccess = false;
19523d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
19533d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
19543d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /* Visible for testing */
1955568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
19560e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        boolean aggregatorEnabled = mContactAggregator.isEnabled();
19573d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mContactAggregator.setEnabled(false);
19583d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        try {
1959bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            if (importer.importContacts()) {
1960bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1961bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                // TODO aggregate all newly added raw contacts
1962bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                mContactAggregator.setEnabled(aggregatorEnabled);
1963bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                return true;
1964bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
19653d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } catch (Throwable e) {
19663d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           Log.e(TAG, "Legacy contact import failed", e);
19673d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
1968bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mEstimatedStorageRequirement = importer.getEstimatedStorageRequirement();
1969bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        return false;
19703d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
19713d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1972a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
1973a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
1974a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
1975a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
19765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsHelper.wipeData();
19775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileHelper.wipeData();
19785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsPhotoStore.clear();
19795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfilePhotoStore.clear();
19803826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatus = ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS;
1981a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1982a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1983568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
198415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov     * During intialization, this content provider will
1985568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * block all attempts to change contacts data. In particular, it will hold
1986568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * up all contact syncs. As soon as the import process is complete, all
1987568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * processes waiting to write to the provider are unblocked and can proceed
1988568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * to compete for the database transaction monitor.
1989568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
199015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private void waitForAccess(CountDownLatch latch) {
199115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (latch == null) {
199215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            return;
199315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        }
199415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
199515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        while (true) {
199615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            try {
199715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                latch.await();
199815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                return;
199915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            } catch (InterruptedException e) {
200015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Thread.currentThread().interrupt();
2001ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            }
2002568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
2003568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2004568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
20055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
20065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Determines whether the given URI should be directed to the profile
20075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * database rather than the contacts database.  This is true under either
20085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * of three conditions:
20095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * 1. The URI itself is specifically for the profile.
20105d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * 2. The URI contains ID references that are in the profile ID-space.
20115d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * 3. The URI contains lookup key references that match the special profile lookup key.
20125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @param uri The URI to examine.
20135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @return Whether to direct the DB operation to the profile database.
20145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
20155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private boolean mapsToProfileDb(Uri uri) {
20165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return sUriMatcher.mapsToProfile(uri);
20175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
20185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
20195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
20205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Determines whether the given URI with the given values being inserted
20215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * should be directed to the profile database rather than the contacts
20225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * database.  This is true if the URI already maps to the profile DB from
20235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * a call to {@link #mapsToProfileDb} or if the URI matches a URI that
20245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * specifies parent IDs via the ContentValues, and the given ContentValues
20255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * contains an ID in the profile ID-space.
20265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @param uri The URI to examine.
20275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @param values The values being inserted.
20285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @return Whether to direct the DB insert to the profile database.
20295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
20305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private boolean mapsToProfileDbWithInsertedValues(Uri uri, ContentValues values) {
20315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDb(uri)) {
20325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return true;
20335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
20345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int match = sUriMatcher.match(uri);
20355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (INSERT_URI_ID_VALUE_MAP.containsKey(match)) {
20365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            String idField = INSERT_URI_ID_VALUE_MAP.get(match);
20375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (values.containsKey(idField)) {
20385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long id = values.getAsLong(idField);
20395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                if (ContactsContract.isProfileId(id)) {
20405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    return true;
20415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                }
20425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
20435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
20445d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return false;
20455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
20465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
20475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
20485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Switches the provider's thread-local context variables to prepare for performing
20495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * a profile operation.
20505d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
20516efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    protected void switchToProfileMode() {
20525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mDbHelper.set(mProfileHelper);
20535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mTransactionContext.set(mProfileTransactionContext);
20545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.set(mProfileAggregator);
20555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mPhotoStore.set(mProfilePhotoStore);
20565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mInProfileMode.set(true);
20575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
20585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
20595d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
20605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Switches the provider's thread-local context variables to prepare for performing
20615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * a contacts operation.
20625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
20636efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    protected void switchToContactMode() {
20645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mDbHelper.set(mContactsHelper);
20655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mTransactionContext.set(mContactTransactionContext);
20665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.set(mContactAggregator);
20675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mPhotoStore.set(mContactsPhotoStore);
20685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mInProfileMode.set(false);
20695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
207036aa19fbab3722288b7f0917166ef6990ab7b52cDave Santoro        // Clear out the active database; modification operations will set this to the contacts DB.
207136aa19fbab3722288b7f0917166ef6990ab7b52cDave Santoro        mActiveDb.set(null);
20725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
20735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
2074568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2075568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
207615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
207736612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
207836612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        // Enforce stream items access check if applicable.
207936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        enforceSocialStreamWritePermission(uri);
208036612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
20815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDbWithInsertedValues(uri, values)) {
2082078f588cef389358adabc579de00747878f3c108Dave Santoro            switchToProfileMode();
2083078f588cef389358adabc579de00747878f3c108Dave Santoro            return mProfileProvider.insert(uri, values);
20845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } else {
20855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToContactMode();
20865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return super.insert(uri, values);
20875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
2088568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2089568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2090568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2091568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
209215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (mWriteAccessLatch != null) {
2093bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // We are stuck trying to upgrade contacts db.  The only update request
2094bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // allowed in this case is an update of provider status, which will trigger
2095bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // an attempt to upgrade contacts again.
2096bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            int match = sUriMatcher.match(uri);
2097bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            if (match == PROVIDER_STATUS) {
2098bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                Integer newStatus = values.getAsInteger(ProviderStatus.STATUS);
2099bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                if (newStatus != null && newStatus == ProviderStatus.STATUS_UPGRADING) {
2100bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    scheduleBackgroundTask(BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS);
2101bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 1;
2102bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                } else {
2103bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 0;
2104bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                }
2105bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
2106bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
210715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
210836612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
210936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        // Enforce stream items access check if applicable.
211036612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        enforceSocialStreamWritePermission(uri);
211136612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
21125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDb(uri)) {
2113078f588cef389358adabc579de00747878f3c108Dave Santoro            switchToProfileMode();
2114078f588cef389358adabc579de00747878f3c108Dave Santoro            return mProfileProvider.update(uri, values, selection, selectionArgs);
21155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } else {
21165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToContactMode();
21175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return super.update(uri, values, selection, selectionArgs);
21185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
2119568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2120568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2121568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2122568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
212315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
212436612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
212536612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        // Enforce stream items access check if applicable.
212636612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        enforceSocialStreamWritePermission(uri);
212736612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
21285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDb(uri)) {
2129078f588cef389358adabc579de00747878f3c108Dave Santoro            switchToProfileMode();
2130078f588cef389358adabc579de00747878f3c108Dave Santoro            return mProfileProvider.delete(uri, selection, selectionArgs);
21315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } else {
21325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToContactMode();
21335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return super.delete(uri, selection, selectionArgs);
21345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
21355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
21365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
21375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
21385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Replaces the current (thread-local) database to use for the operation with the given one.
21395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @param db The database to use.
21405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
21415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /* package */ void substituteDb(SQLiteDatabase db) {
21425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.set(db);
2143568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2144568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2145568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
214682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    public Bundle call(String method, String arg, Bundle extras) {
214782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        waitForAccess(mReadAccessLatch);
214882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        if (method.equals(Authorization.AUTHORIZATION_METHOD)) {
214982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            Uri uri = (Uri) extras.getParcelable(Authorization.KEY_URI_TO_AUTHORIZE);
215082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
215182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // Check permissions on the caller.  The URI can only be pre-authorized if the caller
215282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // already has the necessary permissions.
215382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            enforceSocialStreamReadPermission(uri);
215482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            if (mapsToProfileDb(uri)) {
215582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                mProfileProvider.enforceReadPermission(uri);
215682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            }
215782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
215882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // If there hasn't been a security violation yet, we're clear to pre-authorize the URI.
215982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            Uri authUri = preAuthorizeUri(uri);
216082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            Bundle response = new Bundle();
216182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            response.putParcelable(Authorization.KEY_AUTHORIZED_URI, authUri);
216282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            return response;
216382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        }
216482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        return null;
216582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    }
216682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
216782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    /**
216882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * Pre-authorizes the given URI, adding an expiring permission token to it and placing that
216982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * in our map of pre-authorized URIs.
217082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * @param uri The URI to pre-authorize.
217182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * @return A pre-authorized URI that will not require special permissions to use.
217282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     */
217382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    private Uri preAuthorizeUri(Uri uri) {
217482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        String token = String.valueOf(mRandom.nextLong());
217582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        Uri authUri = uri.buildUpon()
217682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                .appendQueryParameter(PREAUTHORIZED_URI_TOKEN, token)
217782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                .build();
217882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        long expiration = SystemClock.elapsedRealtime() + mPreAuthorizedUriDuration;
217982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        mPreAuthorizedUris.put(authUri, expiration);
218082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
218182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        return authUri;
218282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    }
218382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
218482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    /**
218582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * Checks whether the given URI has an unexpired permission token that would grant access to
218682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * query the content.  If it does, the regular permission check should be skipped.
218782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * @param uri The URI being accessed.
218882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * @return Whether the URI is a pre-authorized URI that is still valid.
218982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     */
219082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    public boolean isValidPreAuthorizedUri(Uri uri) {
219182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        // Only proceed if the URI has a permission token parameter.
219282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        if (uri.getQueryParameter(PREAUTHORIZED_URI_TOKEN) != null) {
219382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // First expire any pre-authorization URIs that are no longer valid.
219482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            long now = SystemClock.elapsedRealtime();
219582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            Set<Uri> expiredUris = Sets.newHashSet();
219682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            for (Uri preAuthUri : mPreAuthorizedUris.keySet()) {
219782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                if (mPreAuthorizedUris.get(preAuthUri) < now) {
219882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                    expiredUris.add(preAuthUri);
219982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                }
220082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            }
220182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            for (Uri expiredUri : expiredUris) {
220282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                mPreAuthorizedUris.remove(expiredUri);
220382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            }
220482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
220582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // Now check to see if the pre-authorized URI map contains the URI.
220682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            if (mPreAuthorizedUris.containsKey(uri)) {
220782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                // Unexpired token - skip the permission check.
220882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                return true;
220982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            }
221082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        }
221182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        return false;
221282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    }
221382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
221482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    @Override
2215078f588cef389358adabc579de00747878f3c108Dave Santoro    protected boolean yield(ContactsTransaction transaction) {
2216078f588cef389358adabc579de00747878f3c108Dave Santoro        // If there's a profile transaction in progress, and we're yielding, we need to
2217078f588cef389358adabc579de00747878f3c108Dave Santoro        // end it.  Unlike the Contacts DB yield (which re-starts a transaction at its
2218078f588cef389358adabc579de00747878f3c108Dave Santoro        // conclusion), we can just go back into a state in which we have no active
2219078f588cef389358adabc579de00747878f3c108Dave Santoro        // profile transaction, and let it be re-created as needed.  We can't hold onto
2220078f588cef389358adabc579de00747878f3c108Dave Santoro        // the transaction without risking a deadlock.
2221078f588cef389358adabc579de00747878f3c108Dave Santoro        SQLiteDatabase profileDb = transaction.removeDbForTag(PROFILE_DB_TAG);
2222078f588cef389358adabc579de00747878f3c108Dave Santoro        if (profileDb != null) {
2223078f588cef389358adabc579de00747878f3c108Dave Santoro            profileDb.setTransactionSuccessful();
2224078f588cef389358adabc579de00747878f3c108Dave Santoro            profileDb.endTransaction();
2225078f588cef389358adabc579de00747878f3c108Dave Santoro        }
2226078f588cef389358adabc579de00747878f3c108Dave Santoro
2227078f588cef389358adabc579de00747878f3c108Dave Santoro        // Now proceed with the Contacts DB yield.
2228078f588cef389358adabc579de00747878f3c108Dave Santoro        SQLiteDatabase contactsDb = transaction.getDbForTag(CONTACTS_DB_TAG);
2229078f588cef389358adabc579de00747878f3c108Dave Santoro        return contactsDb != null && contactsDb.yieldIfContendedSafely(SLEEP_AFTER_YIELD_DELAY);
2230078f588cef389358adabc579de00747878f3c108Dave Santoro    }
2231078f588cef389358adabc579de00747878f3c108Dave Santoro
2232078f588cef389358adabc579de00747878f3c108Dave Santoro    @Override
2233568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
2234568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            throws OperationApplicationException {
223515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
2236078f588cef389358adabc579de00747878f3c108Dave Santoro        return super.applyBatch(operations);
2237568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2238568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
22394f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
22407b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    public int bulkInsert(Uri uri, ContentValues[] values) {
22417b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov        waitForAccess(mWriteAccessLatch);
2242078f588cef389358adabc579de00747878f3c108Dave Santoro        return super.bulkInsert(uri, values);
22437b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    }
22447b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov
22457b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    @Override
2246078f588cef389358adabc579de00747878f3c108Dave Santoro    public void onBegin() {
2247bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2248b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "onBeginTransaction");
2249b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
22505d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (inProfileMode()) {
22515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mProfileAggregator.clearPendingAggregations();
22525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mProfileTransactionContext.clear();
22535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } else {
22545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mContactAggregator.clearPendingAggregations();
22555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mContactTransactionContext.clear();
22565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
2257b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2258b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2259285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2260078f588cef389358adabc579de00747878f3c108Dave Santoro    public void onCommit() {
2261bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2262b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "beforeTransactionCommit");
2263b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2264b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
22655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().aggregateInTransaction(mTransactionContext.get(), mActiveDb.get());
22661a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (mVisibleTouched) {
22671a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = false;
22685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mDbHelper.get().updateAllVisible();
22691a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        }
22703826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
2271bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        updateSearchIndexInTransaction();
2272bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov
22733826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatusUpdateNeeded) {
22743826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
22753826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatusUpdateNeeded = false;
22763826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
2277b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2278b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2279078f588cef389358adabc579de00747878f3c108Dave Santoro    @Override
2280078f588cef389358adabc579de00747878f3c108Dave Santoro    public void onRollback() {
2281078f588cef389358adabc579de00747878f3c108Dave Santoro        // Not used.
2282078f588cef389358adabc579de00747878f3c108Dave Santoro    }
2283078f588cef389358adabc579de00747878f3c108Dave Santoro
2284bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov    private void updateSearchIndexInTransaction() {
22855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> staleContacts = mTransactionContext.get().getStaleSearchIndexContactIds();
22865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> staleRawContacts = mTransactionContext.get().getStaleSearchIndexRawContactIds();
2287bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        if (!staleContacts.isEmpty() || !staleRawContacts.isEmpty()) {
2288bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mSearchIndexManager.updateIndexForRawContacts(staleContacts, staleRawContacts);
22895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mTransactionContext.get().clearSearchIndexUpdates();
2290bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        }
2291bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov    }
2292bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov
2293b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void flushTransactionalChanges() {
2294bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2295b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "flushTransactionChanges");
2296b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
22971129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
22985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        for (long rawContactId : mTransactionContext.get().getInsertedRawContactIds()) {
22995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mDbHelper.get().updateRawContactDisplayName(mActiveDb.get(), rawContactId);
23005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mAggregator.get().onRawContactInsert(mTransactionContext.get(), mActiveDb.get(),
23015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    rawContactId);
230224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
230324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
23045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> dirtyRawContacts = mTransactionContext.get().getDirtyRawContactIds();
2305d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!dirtyRawContacts.isEmpty()) {
2306a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
2307a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_DIRTY_SQL);
2308d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, dirtyRawContacts);
2309a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
23105d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().execSQL(mSb.toString());
2311a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        }
2312a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
23135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> updatedRawContacts = mTransactionContext.get().getUpdatedRawContactIds();
2314d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!updatedRawContacts.isEmpty()) {
2315a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
2316a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_VERSION_SQL);
2317d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, updatedRawContacts);
2318a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
23195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().execSQL(mSb.toString());
2320b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2321b5a4add17815167d20a90645779df34cdf45280dFred Quintana
23225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Update sync states.
23235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        for (Map.Entry<Long, Object> entry : mTransactionContext.get().getUpdatedSyncStates()) {
2324b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long id = entry.getKey();
23255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (mDbHelper.get().getSyncState().update(mActiveDb.get(), id, entry.getValue()) <= 0) {
23269d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                throw new IllegalStateException(
23279d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                        "unable to update sync state, does it still exist?");
23289d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            }
2329b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2330b5a4add17815167d20a90645779df34cdf45280dFred Quintana
23315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mTransactionContext.get().clear();
2332b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2333b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2334a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /**
2335a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * Appends comma separated ids.
2336a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * @param ids Should not be empty
2337a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     */
2338d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov    private void appendIds(StringBuilder sb, Set<Long> ids) {
2339b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long id : ids) {
2340a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            sb.append(id).append(',');
2341b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2342a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
2343a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        sb.setLength(sb.length() - 1); // Yank the last comma
2344285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
2345285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2346285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2347cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    protected void notifyChange() {
234881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        notifyChange(mSyncToNetwork);
234981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = false;
235081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
235181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
235281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    protected void notifyChange(boolean syncToNetwork) {
235381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
235481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                syncToNetwork);
2355cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    }
2356568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
235751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    protected void setProviderStatus(int status) {
23583826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != status) {
23593826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatus = status;
23603826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            getContext().getContentResolver().notifyChange(ProviderStatus.CONTENT_URI, null, false);
23613826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
236251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
236351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
2364f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    public DataRowHandler getDataRowHandler(final String mimeType) {
23655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (inProfileMode()) {
23665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return getDataRowHandlerForProfile(mimeType);
23675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
23683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
23693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
23706d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov            handler = new DataRowHandlerForCustomMimetype(
23715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    getContext(), mContactsHelper, mContactAggregator, mimeType);
23723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
23733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
23743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
23753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
23763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
23775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    public DataRowHandler getDataRowHandlerForProfile(final String mimeType) {
23785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        DataRowHandler handler = mProfileDataRowHandlers.get(mimeType);
23795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (handler == null) {
23805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            handler = new DataRowHandlerForCustomMimetype(
23815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    getContext(), mProfileHelper, mProfileAggregator, mimeType);
23825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mProfileDataRowHandlers.put(mimeType, handler);
23835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
23845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return handler;
23855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
23865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
23874f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2388de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected Uri insertInTransaction(Uri uri, ContentValues values) {
2389bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
23901129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Log.v(TAG, "insertInTransaction: " + uri + " " + values);
2391b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2392f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
23935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
23945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
2395078f588cef389358adabc579de00747878f3c108Dave Santoro            mActiveDb.set(mContactsHelper.getWritableDatabase());
23965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
23975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
2398f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2399f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2400f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2401a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
2402a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
240335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2404a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
240535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
24065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE:
24075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                id = mDbHelper.get().getSyncState().insert(mActiveDb.get(), values);
240835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
240935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2410d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
2411d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                insertContact(values);
24126bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
24136bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
24146bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
241524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
241624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                throw new UnsupportedOperationException(
241724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        "The profile contact is created automatically");
241824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
241924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2420d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS:
2421d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS: {
24225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                id = insertRawContact(uri, values, callerIsSyncAdapter);
2423f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2424a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2425a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2426a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2427d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS_DATA:
2428d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID_DATA: {
2429d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                int segment = match == RAW_CONTACTS_DATA ? 1 : 2;
2430d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(segment));
2431f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2432f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2433a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2434a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2435a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
24363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case RAW_CONTACTS_ID_STREAM_ITEMS: {
24373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                values.put(StreamItems.RAW_CONTACT_ID, uri.getPathSegments().get(1));
24383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItem(uri, values);
24393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
24403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
24413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
24423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24430c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case DATA:
24440c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case PROFILE_DATA: {
2445f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2446f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2447a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2448a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2449a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2450ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
2451f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                id = insertGroup(uri, values, callerIsSyncAdapter);
2452f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2453ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2454ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2455ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2456eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
24575aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertSettings(uri, values);
245843880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2459eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
2460eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2461eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
24625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case STATUS_UPDATES:
24635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_STATUS_UPDATES: {
246482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                id = insertStatusUpdate(values);
24651f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
24661f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
24671f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
24683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
24693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItem(uri, values);
24703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
24713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
24723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
24733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
24753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItemPhoto(uri, values);
24763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
24773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
24783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
24793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
24813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                values.put(StreamItemPhotos.STREAM_ITEM_ID, uri.getPathSegments().get(1));
24823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItemPhoto(uri, values);
24833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
24843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
24853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
24863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
2487a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
248881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
2489f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
2490a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2491a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
24927e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
24937e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
24947e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
24957e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2496de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        return ContentUris.withAppendedId(uri, id);
2497a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2498a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2499a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2500e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * If account is non-null then store it in the values. If the account is
2501e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * already specified in the values then it must be consistent with the
2502e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * account, if it is non-null.
2503e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *
2504e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param uri Current {@link Uri} being operated on.
2505e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param values {@link ContentValues} to read and possibly update.
2506e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when only one of
2507e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_NAME} or
2508e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_TYPE} is specified, leaving the
2509e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             other undefined.
2510e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when {@link RawContacts#ACCOUNT_NAME}
2511e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             and {@link RawContacts#ACCOUNT_TYPE} are inconsistent between
2512e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             the given {@link Uri} and {@link ContentValues}.
25137e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
2514e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey    private Account resolveAccount(Uri uri, ContentValues values) throws IllegalArgumentException {
2515f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
2516f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
2517e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
2518f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2519f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
2520f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
2521e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialValues = TextUtils.isEmpty(valueAccountName)
2522e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                ^ TextUtils.isEmpty(valueAccountType);
2523e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2524e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri || partialValues) {
2525e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
25265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
2527fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
2528e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
2529e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2530e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
2531e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
2532e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validUri = !TextUtils.isEmpty(accountName);
2533e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validValues = !TextUtils.isEmpty(valueAccountName);
2534e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2535e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validValues && validUri) {
2536e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Check that accounts match when both present
2537e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            final boolean accountMatch = TextUtils.equals(accountName, valueAccountName)
2538e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    && TextUtils.equals(accountType, valueAccountType);
2539e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            if (!accountMatch) {
25405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
2541fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        "When both specified, ACCOUNT_NAME and ACCOUNT_TYPE must match", uri));
2542e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            }
2543e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validUri) {
2544e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Fill values from Uri when not present
2545f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_NAME, accountName);
2546f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_TYPE, accountType);
2547e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validValues) {
2548f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountName = valueAccountName;
2549f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountType = valueAccountType;
2550e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else {
2551e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            return null;
2552f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
2553f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2554e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Use cached Account object when matches, otherwise create
2555f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mAccount == null
2556f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.name.equals(accountName)
2557f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.type.equals(accountType)) {
2558f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mAccount = new Account(accountName, accountType);
2559035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
2560f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2561e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        return mAccount;
25627e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
25637e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
25647e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
256543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * Resolves the account and builds an {@link AccountWithDataSet} based on the data set specified
256643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * in the URI or values (if any).
256743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * @param uri Current {@link Uri} being operated on.
256843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * @param values {@link ContentValues} to read and possibly update.
256943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     */
257043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    private AccountWithDataSet resolveAccountWithDataSet(Uri uri, ContentValues values) {
25713593682b8d9213fde576a0cff54458ad50563980Dave Santoro        final Account account = resolveAccount(uri, values);
257243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        AccountWithDataSet accountWithDataSet = null;
257343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        if (account != null) {
257443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            String dataSet = getQueryParameter(uri, RawContacts.DATA_SET);
257543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            if (dataSet == null) {
25763593682b8d9213fde576a0cff54458ad50563980Dave Santoro                dataSet = values.getAsString(RawContacts.DATA_SET);
2577a71dc460ca951c7aca591f3f470c160cde70a1e3Dave Santoro            } else {
25783593682b8d9213fde576a0cff54458ad50563980Dave Santoro                values.put(RawContacts.DATA_SET, dataSet);
257943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
258043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            accountWithDataSet = new AccountWithDataSet(account.name, account.type, dataSet);
258143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        }
258243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        return accountWithDataSet;
258343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    }
258443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
258543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    /**
2586d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Inserts an item in the contacts table
25876bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
25886bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
25896bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
25906bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
2591d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertContact(ContentValues values) {
2592de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregate contacts are created automatically");
25936bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
25946bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
25956bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
259624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Inserts an item in the raw contacts table
2597a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2598f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param uri the values for the new row
2599f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param values the account this contact should be associated with. may be null.
2600dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana     * @param callerIsSyncAdapter
2601a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2602a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
26035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private long insertRawContact(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
2604f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2605f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2606f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2607f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
260843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        AccountWithDataSet accountWithDataSet = resolveAccountWithDataSet(uri, mValues);
26097e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
26103d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
26113d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
2612f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
26133d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
26143d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
26155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        long rawContactId = mActiveDb.get().insert(Tables.RAW_CONTACTS,
26165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                RawContacts.CONTACT_ID, mValues);
2617f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        int aggregationMode = RawContacts.AGGREGATION_MODE_DEFAULT;
26185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mValues.containsKey(RawContacts.AGGREGATION_MODE)) {
2619f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            aggregationMode = mValues.getAsInteger(RawContacts.AGGREGATION_MODE);
2620f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        }
26215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().markNewForAggregation(rawContactId, aggregationMode);
2622285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
26235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Trigger creation of a Contact based on this RawContact at the end of transaction
26245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mTransactionContext.get().rawContactInserted(rawContactId, accountWithDataSet);
2625f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2626dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter) {
2627dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            addAutoAddMembership(rawContactId);
2628dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            final Long starred = values.getAsLong(RawContacts.STARRED);
2629dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (starred != null && starred != 0) {
2630dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateFavoritesMembership(rawContactId, starred != 0);
2631dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2632dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2633dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
26343826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
2635023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        return rawContactId;
2636a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2637a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2638dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void addAutoAddMembership(long rawContactId) {
2639dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID,
2640dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2641dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2642dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            insertDataGroupMembership(rawContactId, groupId);
2643dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2644dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2645dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2646dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private Long findGroupByRawContactId(String selection, long rawContactId) {
26475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = mActiveDb.get().query(Tables.GROUPS + "," + Tables.RAW_CONTACTS,
26485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                PROJECTION_GROUP_ID, selection,
2649dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                new String[]{Long.toString(rawContactId)},
2650dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                null /* groupBy */, null /* having */, null /* orderBy */);
2651dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        try {
2652dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            while (c.moveToNext()) {
2653dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return c.getLong(0);
2654dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2655dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return null;
2656dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        } finally {
2657dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            c.close();
2658dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2659dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2660dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2661dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void updateFavoritesMembership(long rawContactId, boolean isStarred) {
2662dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID,
2663dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2664dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2665dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (isStarred) {
2666dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                insertDataGroupMembership(rawContactId, groupId);
2667dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
2668dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                deleteDataGroupMembership(rawContactId, groupId);
2669dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2670dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2671dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2672dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2673dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void insertDataGroupMembership(long rawContactId, long groupId) {
2674dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        ContentValues groupMembershipValues = new ContentValues();
2675dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.GROUP_ROW_ID, groupId);
2676dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.RAW_CONTACT_ID, rawContactId);
2677dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(DataColumns.MIMETYPE_ID,
26785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mDbHelper.get().getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
26795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.get().insert(Tables.DATA, null, groupMembershipValues);
2680dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2681dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2682dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void deleteDataGroupMembership(long rawContactId, long groupId) {
2683dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final String[] selectionArgs = {
26845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Long.toString(mDbHelper.get().getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE)),
2685dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(groupId),
2686dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(rawContactId)};
26875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.get().delete(Tables.DATA, SELECTION_GROUPMEMBERSHIP_DATA, selectionArgs);
2688dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2689dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2690a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2691a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
2692a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2693a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
2694a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2695a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2696f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
2697a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
2698de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.clear();
2699de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.putAll(values);
270067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
2701de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
270220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2703de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace package with internal mapping
2704de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
2705de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (packageName != null) {
27065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.get().getPackageId(packageName));
2707de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
2708de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.RES_PACKAGE);
2709508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2710de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace mimetype with internal mapping
2711de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String mimeType = mValues.getAsString(Data.MIMETYPE);
2712de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (TextUtils.isEmpty(mimeType)) {
2713de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
2714de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
27154097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
27165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.get().getMimeTypeId(mimeType));
2717de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
2718a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2719a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
27205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        id = rowHandler.insert(mActiveDb.get(), mTransactionContext.get(), rawContactId, mValues);
2721f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
27225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mTransactionContext.get().markRawContactDirty(rawContactId);
2723a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
27245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mTransactionContext.get().rawContactUpdated(rawContactId);
2725a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
27264f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
27274f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
27283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
27293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Inserts an item in the stream_items table.  The account is checked against the
27303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account in the raw contact for which the stream item is being inserted.  If the
27313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * new stream item results in more stream items under this raw contact than the limit,
27323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the oldest one will be deleted (note that if the stream item inserted was the
27333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * oldest, it will be immediately deleted, and this will return 0).
27343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
27353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param uri the insertion URI
27363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param values the values for the new row
27373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return the stream item _ID of the newly created row, or 0 if it was not created
27383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
27393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long insertStreamItem(Uri uri, ContentValues values) {
27403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long id = 0;
27413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.clear();
27423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.putAll(values);
27433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long rawContactId = mValues.getAsLong(StreamItems.RAW_CONTACT_ID);
27453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Ensure that the raw contact exists and belongs to the caller's account.
27473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, mValues);
27483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccount(account, rawContactId);
27493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27506802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to insert accounts params - they don't exist in the stream items table.
27516802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        mValues.remove(RawContacts.ACCOUNT_NAME);
27526802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        mValues.remove(RawContacts.ACCOUNT_TYPE);
27536802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
27543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Insert the new stream item.
27555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        id = mActiveDb.get().insert(Tables.STREAM_ITEMS, null, mValues);
27566802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (id == -1) {
27576802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Insertion failed.
27586802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return 0;
27596802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
27603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check to see if we're over the limit for stream items under this raw contact.
27623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // It's possible that the inserted stream item is older than the the existing
27633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // ones, in which case it may be deleted immediately (resetting the ID to 0).
27643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        id = cleanUpOldStreamItems(rawContactId, id);
27653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return id;
27673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
27683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
27703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Inserts an item in the stream_item_photos table.  The account is checked against
27713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the account in the raw contact that owns the stream item being modified.
27723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
27733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param uri the insertion URI
27743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param values the values for the new row
27756802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @return the stream item photo _ID of the newly created row, or 0 if there was an issue
27766802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     *     with processing the photo or creating the row
27773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
27783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long insertStreamItemPhoto(Uri uri, ContentValues values) {
27793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long id = 0;
27803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.clear();
27813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.putAll(values);
27823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long streamItemId = mValues.getAsLong(StreamItemPhotos.STREAM_ITEM_ID);
27843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        if (streamItemId != 0) {
27853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            long rawContactId = lookupRawContactIdForStreamId(streamItemId);
27863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            // Ensure that the raw contact exists and belongs to the caller's account.
27883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            Account account = resolveAccount(uri, mValues);
27893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            enforceModifyingAccount(account, rawContactId);
27903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27916802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Don't attempt to insert accounts params - they don't exist in the stream item
27926802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // photos table.
27936802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            mValues.remove(RawContacts.ACCOUNT_NAME);
27946802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            mValues.remove(RawContacts.ACCOUNT_TYPE);
27953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27966802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Process the photo and store it.
27976802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            if (processStreamItemPhoto(mValues, false)) {
27986802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // Insert the stream item photo.
27995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                id = mActiveDb.get().insert(Tables.STREAM_ITEM_PHOTOS, null, mValues);
28006802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
28013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
28023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return id;
28033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
28043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
28053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
28066802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * Processes the photo contained in the {@link ContactsContract.StreamItemPhotos#PHOTO}
28076802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * field of the given values, attempting to store it in the photo store.  If successful,
28086802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * the resulting photo file ID will be added to the values for insert/update in the table.
28096802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * <p>
28106802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * If updating, it is valid for the picture to be empty or unspecified (the function will
28116802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * still return true).  If inserting, a valid picture must be specified.
28126802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @param values The content values provided by the caller.
28136802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @param forUpdate Whether this photo is being processed for update (vs. insert).
28146802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @return Whether the insert or update should proceed.
28156802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     */
28166802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    private boolean processStreamItemPhoto(ContentValues values, boolean forUpdate) {
28176802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (!values.containsKey(StreamItemPhotos.PHOTO)) {
28186802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return forUpdate;
28196802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
28206802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        byte[] photoBytes = values.getAsByteArray(StreamItemPhotos.PHOTO);
28216802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (photoBytes == null) {
28226802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return forUpdate;
28236802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
28246802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
28256802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Process the photo and store it.
28266802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        try {
28275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            long photoFileId = mPhotoStore.get().insert(new PhotoProcessor(photoBytes,
28281dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                    mMaxDisplayPhotoDim, mMaxThumbnailPhotoDim, true), true);
28296802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            if (photoFileId != 0) {
28306802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                values.put(StreamItemPhotos.PHOTO_FILE_ID, photoFileId);
28316802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                values.remove(StreamItemPhotos.PHOTO);
28326802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                return true;
28336802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            } else {
28346802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // Couldn't store the photo, return 0.
28356802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                Log.e(TAG, "Could not process stream item photo for insert");
28366802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                return false;
28376802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
28386802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        } catch (IOException ioe) {
28396802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            Log.e(TAG, "Could not process stream item photo for insert", ioe);
28406802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return false;
28416802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
28426802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    }
28436802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
28446802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    /**
28453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Looks up the raw contact ID that owns the specified stream item.
28463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param streamItemId The ID of the stream item.
28473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The associated raw contact ID, or -1 if no such stream item exists.
28483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
28493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long lookupRawContactIdForStreamId(long streamItemId) {
28503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long rawContactId = -1;
28515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = mActiveDb.get().query(Tables.STREAM_ITEMS,
28525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new String[]{StreamItems.RAW_CONTACT_ID},
28533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                StreamItems._ID + "=?", new String[]{String.valueOf(streamItemId)},
28543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                null, null, null);
28553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
28563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if (c.moveToFirst()) {
28573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                rawContactId = c.getLong(0);
28583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
28593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
28603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
28613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
28623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return rawContactId;
28633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
28643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
28653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
286636612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     * If the given URI is reading stream items or stream photos, this will run a permission check
286736612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     * for the android.permission.READ_SOCIAL_STREAM permission - otherwise it will do nothing.
286836612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     * @param uri The URI to check.
286936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     */
287036612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    private void enforceSocialStreamReadPermission(Uri uri) {
287182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        if (SOCIAL_STREAM_URIS.contains(sUriMatcher.match(uri))
287282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                && !isValidPreAuthorizedUri(uri)) {
287336612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            getContext().enforceCallingOrSelfPermission(
287436612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro                    "android.permission.READ_SOCIAL_STREAM", null);
287536612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        }
287636612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    }
287736612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
287836612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    /**
287936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     * If the given URI is modifying stream items or stream photos, this will run a permission check
288036612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     * for the android.permission.WRITE_SOCIAL_STREAM permission - otherwise it will do nothing.
288136612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     * @param uri The URI to check.
288236612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     */
288336612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    private void enforceSocialStreamWritePermission(Uri uri) {
288436612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        if (SOCIAL_STREAM_URIS.contains(sUriMatcher.match(uri))) {
288536612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            getContext().enforceCallingOrSelfPermission(
288636612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro                    "android.permission.WRITE_SOCIAL_STREAM", null);
288736612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        }
288836612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    }
288936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
289036612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    /**
28913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given raw contact ID is owned by the given account.
28923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * If the resolved account is null, this will return true iff the raw contact
28933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * is also associated with the "null" account.
28943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
28953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * If the resolved account does not match, this will throw a security exception.
28963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
28973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param rawContactId The raw contact ID to check for.
28983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
28993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void enforceModifyingAccount(Account account, long rawContactId) {
29003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        String accountSelection = RawContactsColumns.CONCRETE_ID + "=? AND "
29013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_NAME + "=? AND "
29023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + "=?";
29033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        String noAccountSelection = RawContactsColumns.CONCRETE_ID + "=? AND "
29043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " IS NULL AND "
29053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " IS NULL";
29063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c;
29073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        if (account != null) {
29085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            c = mActiveDb.get().query(Tables.RAW_CONTACTS,
29095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    new String[]{RawContactsColumns.CONCRETE_ID}, accountSelection,
29103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    new String[]{String.valueOf(rawContactId), mAccount.name, mAccount.type},
29113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    null, null, null);
29123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } else {
29135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            c = mActiveDb.get().query(Tables.RAW_CONTACTS,
29145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    new String[]{RawContactsColumns.CONCRETE_ID}, noAccountSelection,
29155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    new String[]{String.valueOf(rawContactId)},
29163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    null, null, null);
29173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
29183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
29193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if(c.getCount() == 0) {
29203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                throw new SecurityException("Caller account does not match raw contact ID "
29213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    + rawContactId);
29223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
29233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
29243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
29253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
29263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
29273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
29293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given selection of stream items matches up with the given
29303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account.  If any of the raw contacts fail the account check, this will throw a
29313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * security exception.
29323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
29333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selection The selection.
29343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selectionArgs The selection arguments.
29353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The list of stream item IDs that would be included in this selection.
29363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
29373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private List<Long> enforceModifyingAccountForStreamItems(Account account, String selection,
29383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
29393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemIds = Lists.newArrayList();
29403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
29413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        setTablesAndProjectionMapForStreamItems(qb);
29425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = qb.query(mActiveDb.get(),
29433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{StreamItems._ID, StreamItems.RAW_CONTACT_ID},
29443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selection, selectionArgs, null, null, null);
29453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
29463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            while (c.moveToNext()) {
29473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                streamItemIds.add(c.getLong(0));
29483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Throw a security exception if the account doesn't match the raw contact's.
29503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                enforceModifyingAccount(account, c.getLong(1));
29513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
29523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
29533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
29543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
29553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemIds;
29563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
29573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
29593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given selection of stream item photos matches up with the given
29603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account.  If any of the raw contacts fail the account check, this will throw a
29613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * security exception.
29623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
29633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selection The selection.
29643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selectionArgs The selection arguments.
29653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The list of stream item photo IDs that would be included in this selection.
29663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
29673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private List<Long> enforceModifyingAccountForStreamItemPhotos(Account account, String selection,
29683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
29693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemPhotoIds = Lists.newArrayList();
29703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
29713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        setTablesAndProjectionMapForStreamItemPhotos(qb);
29725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = qb.query(mActiveDb.get(),
29735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new String[]{StreamItemPhotos._ID, StreamItems.RAW_CONTACT_ID},
29743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selection, selectionArgs, null, null, null);
29753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
29763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            while (c.moveToNext()) {
29773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                streamItemPhotoIds.add(c.getLong(0));
29783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Throw a security exception if the account doesn't match the raw contact's.
29803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                enforceModifyingAccount(account, c.getLong(1));
29813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
29823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
29833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
29843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
29853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemPhotoIds;
29863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
29873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
29893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Queries the database for stream items under the given raw contact.  If there are
29903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * more entries than {@link ContactsProvider2#MAX_STREAM_ITEMS_PER_RAW_CONTACT},
29913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the oldest entries (as determined by timestamp) will be deleted.
29923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param rawContactId The raw contact ID to examine for stream items.
29933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param insertedStreamItemId The ID of the stream item that was just inserted,
29943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     prompting this cleanup.  Callers may pass 0 if no insertion prompted the
29953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     cleanup.
29963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The ID of the inserted stream item if it still exists after cleanup;
29973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     0 otherwise.
29983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
29993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long cleanUpOldStreamItems(long rawContactId, long insertedStreamItemId) {
30003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long postCleanupInsertedStreamId = insertedStreamItemId;
30015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = mActiveDb.get().query(Tables.STREAM_ITEMS, new String[]{StreamItems._ID},
30023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                StreamItems.RAW_CONTACT_ID + "=?", new String[]{String.valueOf(rawContactId)},
30033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                null, null, StreamItems.TIMESTAMP + " DESC, " + StreamItems._ID + " DESC");
30043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
30053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            int streamItemCount = c.getCount();
30063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if (streamItemCount <= MAX_STREAM_ITEMS_PER_RAW_CONTACT) {
30073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Still under the limit - nothing to clean up!
30083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return insertedStreamItemId;
30093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            } else {
30103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                c.moveToLast();
30113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                while (c.getPosition() >= MAX_STREAM_ITEMS_PER_RAW_CONTACT) {
30123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    long streamItemId = c.getLong(0);
30133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    if (insertedStreamItemId == streamItemId) {
30143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        // The stream item just inserted is being deleted.
30153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        postCleanupInsertedStreamId = 0;
30163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    }
30173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    deleteStreamItem(c.getLong(0));
30183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    c.moveToPrevious();
30193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
30203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
30213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
30223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
30233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
30243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return postCleanupInsertedStreamId;
30253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
30263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
30279261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
302820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
302920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
3030f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
303120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
303220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3033de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
3034de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
30350c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro        Uri dataUri = inProfileMode()
30360c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro                ? Uri.withAppendedPath(Profile.CONTENT_URI, RawContacts.Data.CONTENT_DIRECTORY)
30370c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro                : Data.CONTENT_URI;
30380c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro        Cursor c = query(dataUri, DataRowHandler.DataDeleteQuery.COLUMNS,
3039f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                selection, selectionArgs, null);
3040de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        try {
3041de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            while(c.moveToNext()) {
3042f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                long rawContactId = c.getLong(DataRowHandler.DataDeleteQuery.RAW_CONTACT_ID);
3043f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
3044a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                DataRowHandler rowHandler = getDataRowHandler(mimeType);
30455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                count += rowHandler.delete(mActiveDb.get(), mTransactionContext.get(), c);
3046f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                if (!callerIsSyncAdapter) {
30475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mTransactionContext.get().markRawContactDirty(rawContactId);
304888e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                }
304920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
305020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
3051de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            c.close();
305220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
305320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
305420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
305520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
305620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
305788e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    /**
305888e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     * Delete a data row provided that it is one of the allowed mime types.
305988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     */
306020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
3061f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
306288e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
306388e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
30644da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(dataId);
3065f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataRowHandler.DataDeleteQuery.COLUMNS, Data._ID + "=?",
30664da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1, null);
3067f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
306820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
306920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
307020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
307120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
307220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3073f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov            String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
307420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
307520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
307620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
307720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
307820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
307920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
308020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
308120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
308220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
30837a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException("Data type mismatch: expected "
308420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
308520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3086a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            DataRowHandler rowHandler = getDataRowHandler(mimeType);
30875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return rowHandler.delete(mActiveDb.get(), mTransactionContext.get(), c);
308820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
308920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
309020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
309120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
309220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
309320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
3094ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
3095ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
3096f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private long insertGroup(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
3097f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
3098f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
3099f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
31003593682b8d9213fde576a0cff54458ad50563980Dave Santoro        final AccountWithDataSet accountWithDataSet = resolveAccountWithDataSet(uri, mValues);
3101ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3102ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
3103f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String packageName = mValues.getAsString(Groups.RES_PACKAGE);
310467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
31055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mValues.put(GroupsColumns.PACKAGE_ID, mDbHelper.get().getPackageId(packageName));
310667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
3107f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.remove(Groups.RES_PACKAGE);
3108ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3109dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final boolean isFavoritesGroup = mValues.getAsLong(Groups.FAVORITES) != null
3110dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                ? mValues.getAsLong(Groups.FAVORITES) != 0
3111dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                : false;
3112dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3113f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
3114f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(Groups.DIRTY, 1);
311573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
311673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
31175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        long result = mActiveDb.get().insert(Tables.GROUPS, Groups.TITLE, mValues);
3118ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
3119dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter && isFavoritesGroup) {
3120dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // add all starred raw contacts to this group
3121dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String selection;
3122dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs;
31233593682b8d9213fde576a0cff54458ad50563980Dave Santoro            if (accountWithDataSet == null) {
3124dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + " IS NULL AND "
312543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + RawContacts.ACCOUNT_TYPE + " IS NULL AND "
312643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + RawContacts.DATA_SET + " IS NULL";
3127dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = null;
31283593682b8d9213fde576a0cff54458ad50563980Dave Santoro            } else if (accountWithDataSet.getDataSet() == null) {
3129dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + "=? AND "
31303593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        + RawContacts.ACCOUNT_TYPE + "=? AND "
31313593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        + RawContacts.DATA_SET + " IS NULL";
31323593682b8d9213fde576a0cff54458ad50563980Dave Santoro                selectionArgs = new String[] {
31333593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getAccountName(),
31343593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getAccountType()
31353593682b8d9213fde576a0cff54458ad50563980Dave Santoro                };
313643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            } else {
313743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                selection = RawContacts.ACCOUNT_NAME + "=? AND "
313843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + RawContacts.ACCOUNT_TYPE + "=? AND "
313943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + RawContacts.DATA_SET + "=?";
31403593682b8d9213fde576a0cff54458ad50563980Dave Santoro                selectionArgs = new String[] {
31413593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getAccountName(),
31423593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getAccountType(),
31433593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getDataSet()
31443593682b8d9213fde576a0cff54458ad50563980Dave Santoro                };
3145dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
31465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS,
3147dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[]{RawContacts._ID, RawContacts.STARRED},
3148dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    selection, selectionArgs, null, null, null);
3149892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            try {
3150892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                while (c.moveToNext()) {
3151892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (c.getLong(1) != 0) {
3152892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        final long rawContactId = c.getLong(0);
3153892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        insertDataGroupMembership(rawContactId, result);
31545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mTransactionContext.get().markRawContactDirty(rawContactId);
3155892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    }
3156dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
3157892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            } finally {
3158892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                c.close();
3159dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
3160dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
3161dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3162f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mValues.containsKey(Groups.GROUP_VISIBLE)) {
31631a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3164ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        }
3165ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
3166ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        return result;
3167ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
3168ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
31695aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private long insertSettings(Uri uri, ContentValues values) {
3170f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        // Before inserting, ensure that no settings record already exists for the
3171f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        // values being inserted (this used to be enforced by a primary key, but that no
3172f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        // longer works with the nullable data_set field added).
3173f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        String accountName = values.getAsString(Settings.ACCOUNT_NAME);
3174f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        String accountType = values.getAsString(Settings.ACCOUNT_TYPE);
3175f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        String dataSet = values.getAsString(Settings.DATA_SET);
3176f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        Uri.Builder settingsUri = Settings.CONTENT_URI.buildUpon();
3177f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        if (accountName != null) {
3178f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            settingsUri.appendQueryParameter(Settings.ACCOUNT_NAME, accountName);
3179f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        }
3180f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        if (accountType != null) {
3181f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            settingsUri.appendQueryParameter(Settings.ACCOUNT_TYPE, accountType);
3182f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        }
3183f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        if (dataSet != null) {
3184f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            settingsUri.appendQueryParameter(Settings.DATA_SET, dataSet);
3185f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        }
3186f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        Cursor c = queryLocal(settingsUri.build(), null, null, null, null, 0);
3187f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        try {
3188f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            if (c.getCount() > 0) {
31890e21a867a572679d64d79041eb574d13665178d4Dave Santoro                // If a record was found, replace it with the new values.
31900e21a867a572679d64d79041eb574d13665178d4Dave Santoro                String selection = null;
31910e21a867a572679d64d79041eb574d13665178d4Dave Santoro                String[] selectionArgs = null;
31920e21a867a572679d64d79041eb574d13665178d4Dave Santoro                if (accountName != null && accountType != null) {
31930e21a867a572679d64d79041eb574d13665178d4Dave Santoro                    selection = Settings.ACCOUNT_NAME + "=? AND " + Settings.ACCOUNT_TYPE + "=?";
31940e21a867a572679d64d79041eb574d13665178d4Dave Santoro                    if (dataSet == null) {
31950e21a867a572679d64d79041eb574d13665178d4Dave Santoro                        selection += " AND " + Settings.DATA_SET + " IS NULL";
31960e21a867a572679d64d79041eb574d13665178d4Dave Santoro                        selectionArgs = new String[] {accountName, accountType};
31970e21a867a572679d64d79041eb574d13665178d4Dave Santoro                    } else {
31980e21a867a572679d64d79041eb574d13665178d4Dave Santoro                        selection += " AND " + Settings.DATA_SET + "=?";
31990e21a867a572679d64d79041eb574d13665178d4Dave Santoro                        selectionArgs = new String[] {accountName, accountType, dataSet};
32000e21a867a572679d64d79041eb574d13665178d4Dave Santoro                    }
32010e21a867a572679d64d79041eb574d13665178d4Dave Santoro                }
32020e21a867a572679d64d79041eb574d13665178d4Dave Santoro                return updateSettings(uri, values, selection, selectionArgs);
3203f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            }
3204f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        } finally {
3205f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            c.close();
3206f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        }
3207f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro
3208f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        // If we didn't find a duplicate, we're fine to insert.
32095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        final long id = mActiveDb.get().insert(Tables.SETTINGS, null, values);
32105aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
32111a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
32121a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3213e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
32141a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
3215e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return id;
3216e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3217e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3218ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
321982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov     * Inserts a status update.
32201f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
322182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    public long insertStatusUpdate(ContentValues values) {
322282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        final String handle = values.getAsString(StatusUpdates.IM_HANDLE);
32230a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        final Integer protocol = values.getAsInteger(StatusUpdates.PROTOCOL);
32244dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        String customProtocol = null;
32254dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
32260a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        if (protocol != null && protocol == Im.PROTOCOL_CUSTOM) {
322782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            customProtocol = values.getAsString(StatusUpdates.CUSTOM_PROTOCOL);
32284dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (TextUtils.isEmpty(customProtocol)) {
32294dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                throw new IllegalArgumentException(
32304dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                        "CUSTOM_PROTOCOL is required when PROTOCOL=PROTOCOL_CUSTOM");
32314dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
32321f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
32331f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3234dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long rawContactId = -1;
3235dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long contactId = -1;
323682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Long dataId = values.getAsLong(StatusUpdates.DATA_ID);
32376802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        String accountType = null;
32386802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        String accountName = null;
3239f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.setLength(0);
32402526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov        mSelectionArgs.clear();
3241dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        if (dataId != null) {
3242dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the contact info for the given data row.
3243dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
32442526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSb.append(Tables.DATA + "." + Data._ID + "=?");
32452526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSelectionArgs.add(String.valueOf(dataId));
32461f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
3247dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the data row to attach this presence update to
3248dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
32490a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(handle) || protocol == null) {
32500a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                throw new IllegalArgumentException("PROTOCOL and IM_HANDLE are required");
32510a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
32520a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
3253dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // TODO: generalize to allow other providers to match against email
3254dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == protocol;
3255dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
32565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            String mimeTypeIdIm = String.valueOf(mDbHelper.get().getMimeTypeIdForIm());
3257dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            if (matchEmail) {
32585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                String mimeTypeIdEmail = String.valueOf(mDbHelper.get().getMimeTypeIdForEmail());
3259f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
3260f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // The following hack forces SQLite to use the (mimetype_id,data1) index, otherwise
3261f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the "OR" conjunction confuses it and it switches to a full scan of
3262f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the raw_contacts table.
3263f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
3264f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // This code relies on the fact that Im.DATA and Email.DATA are in fact the same
3265f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // column - Data.DATA1
32662526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + " IN (?,?)" +
32672526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Data.DATA1 + "=?" +
32682526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND ((" + DataColumns.MIMETYPE_ID + "=? AND " + Im.PROTOCOL + "=?");
32692526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
32702526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
32712526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
32722526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
32732526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
3274dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
32752526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
32762526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
3277dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
32782526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(") OR (" + DataColumns.MIMETYPE_ID + "=?))");
32792526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
3280dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            } else {
32812526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + "=?" +
32822526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.PROTOCOL + "=?" +
32832526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.DATA + "=?");
32842526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
32852526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
32862526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
3287dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
32882526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
32892526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
3290dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
3291dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
32921f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
329382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.DATA_ID)) {
32942526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(" AND " + DataColumns.CONCRETE_ID + "=?");
32952526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(values.getAsString(StatusUpdates.DATA_ID));
3296dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
329770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
329870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
32991f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
33001f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
33015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            cursor = mActiveDb.get().query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
33022526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.toString(), mSelectionArgs.toArray(EMPTY_STRING_ARRAY), null, null,
33034394086494fe7909aaca70f56fb4bb08beebf303Dmitri Plotnikov                    Clauses.CONTACT_VISIBLE + " DESC, " + Data.RAW_CONTACT_ID);
33041f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
330567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
33065ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
33076802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                accountType = cursor.getString(DataContactsQuery.ACCOUNT_TYPE);
33086802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                accountName = cursor.getString(DataContactsQuery.ACCOUNT_NAME);
3309e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
33101f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
33111f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
33121f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
33131f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
33141f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
331531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
331631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
331731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
33181f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
33191f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
332082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.PRESENCE)) {
3321a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (customProtocol == null) {
3322a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // We cannot allow a null in the custom protocol field, because SQLite3 does not
3323a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // properly enforce uniqueness of null values
3324a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                customProtocol = "";
3325a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
3326a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3327a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.clear();
332882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.DATA_ID, dataId);
3329a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.RAW_CONTACT_ID, rawContactId);
3330a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.CONTACT_ID, contactId);
333182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PROTOCOL, protocol);
333282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
333382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.IM_HANDLE, handle);
333482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.IM_ACCOUNT)) {
333582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                mValues.put(StatusUpdates.IM_ACCOUNT, values.getAsString(StatusUpdates.IM_ACCOUNT));
3336a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
333782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PRESENCE,
333882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    values.getAsString(StatusUpdates.PRESENCE));
3339aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori            mValues.put(StatusUpdates.CHAT_CAPABILITY,
3340aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                    values.getAsString(StatusUpdates.CHAT_CAPABILITY));
33411f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3342a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            // Insert the presence update
33435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().replace(Tables.PRESENCE, null, mValues);
3344a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3345e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
33460a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
334782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.STATUS)) {
334882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String status = values.getAsString(StatusUpdates.STATUS);
33490a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String resPackage = values.getAsString(StatusUpdates.STATUS_RES_PACKAGE);
33500bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            Resources resources = getContext().getResources();
33510bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            if (!TextUtils.isEmpty(resPackage)) {
33520bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                PackageManager pm = getContext().getPackageManager();
33530bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                try {
33540bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                    resources = pm.getResourcesForApplication(resPackage);
33550bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                } catch (NameNotFoundException e) {
33560bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                    Log.w(TAG, "Contact status update resource package not found: "
33570bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                            + resPackage);
33580bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                }
33590bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            }
33600bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            Integer labelResourceId = values.getAsInteger(StatusUpdates.STATUS_LABEL);
33610a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
33620bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            if ((labelResourceId == null || labelResourceId == 0) && protocol != null) {
33630bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                labelResourceId = Im.getProtocolLabelResource(protocol);
33640a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
33650bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            String labelResource = getResourceName(resources, "string", labelResourceId);
33660a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
33670bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            Integer iconResourceId = values.getAsInteger(StatusUpdates.STATUS_ICON);
33680a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            // TODO compute the default icon based on the protocol
33690a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
33700bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            String iconResource = getResourceName(resources, "drawable", iconResourceId);
33710bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann
3372a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (TextUtils.isEmpty(status)) {
33735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mDbHelper.get().deleteStatusUpdate(dataId);
3374a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            } else {
33756802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                Long timestamp = values.getAsLong(StatusUpdates.STATUS_TIMESTAMP);
33766802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (timestamp != null) {
33775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mDbHelper.get().replaceStatusUpdate(dataId, timestamp, status, resPackage,
33780bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                            iconResourceId, labelResourceId);
33796802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                } else {
33805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mDbHelper.get().insertStatusUpdate(dataId, status, resPackage, iconResourceId,
33810bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                            labelResourceId);
33826802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                }
33836802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
33846802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // For forward compatibility with the new stream item API, insert this status update
33856802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // there as well.  If we already have a stream item from this source, update that
33866802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // one instead of inserting a new one (since the semantics of the old status update
33876802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // API is to only have a single record).
33886802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (rawContactId != -1 && !TextUtils.isEmpty(status)) {
33896802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    ContentValues streamItemValues = new ContentValues();
33906802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RAW_CONTACT_ID, rawContactId);
3391d5ef5903570e533a501abe6a8e3d533fdb5318fcFlavio Lerda                    // Status updates are text only but stream items are HTML.
3392e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda                    streamItemValues.put(StreamItems.TEXT, statusUpdateToHtml(status));
33936802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.COMMENTS, "");
33946802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_PACKAGE, resPackage);
33956802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_ICON, iconResource);
33966802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_LABEL, labelResource);
33976802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.TIMESTAMP,
33986802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            timestamp == null ? System.currentTimeMillis() : timestamp);
33996802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
34006802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // Note: The following is basically a workaround for the fact that status
34016802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // updates didn't do any sort of account enforcement, while social stream item
34026802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // updates do.  We can't expect callers of the old API to start passing account
34036802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // information along, so we just populate the account params appropriately for
340443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    // the raw contact.  Data set is not relevant here, as we only check account
340543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    // name and type.
34066802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    if (accountName != null && accountType != null) {
34076802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        streamItemValues.put(RawContacts.ACCOUNT_NAME, accountName);
34086802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        streamItemValues.put(RawContacts.ACCOUNT_TYPE, accountType);
34096802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    }
34106802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
34116802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // Check for an existing stream item from this source, and insert or update.
34126802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    Uri streamUri = StreamItems.CONTENT_URI;
341336612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro                    Cursor c = queryLocal(streamUri, new String[]{StreamItems._ID},
34146802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            StreamItems.RAW_CONTACT_ID + "=?",
341536612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro                            new String[]{String.valueOf(rawContactId)},
341636612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro                            null, -1 /* directory ID */);
34176802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    try {
34186802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        if (c.getCount() > 0) {
34196802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            c.moveToFirst();
342036612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro                            updateInTransaction(ContentUris.withAppendedId(streamUri, c.getLong(0)),
34216802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    streamItemValues, null, null);
34226802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        } else {
342336612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro                            insertInTransaction(streamUri, streamItemValues);
34246802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        }
34256802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    } finally {
34266802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        c.close();
34276802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    }
34286802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                }
3429e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            }
3430e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        }
3431bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
3432a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (contactId != -1) {
34335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mAggregator.get().updateLastStatusUpdateId(contactId);
3434a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3435a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3436a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        return dataId;
34371f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
34381f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3439e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda    /** Converts a status update to HTML. */
3440e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda    private String statusUpdateToHtml(String status) {
34414747809486541f7a3d342d3e1dd48fb5ea255ad6Flavio Lerda        return TextUtils.htmlEncode(status);
3442e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda    }
3443e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda
34440bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann    private String getResourceName(Resources resources, String expectedType, Integer resourceId) {
34450bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann        try {
34460bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            if (resourceId == null || resourceId == 0) return null;
34470bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann
34480bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            // Resource has an invalid type (e.g. a string as icon)? ignore
34490bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            final String resourceEntryName = resources.getResourceEntryName(resourceId);
34500bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            final String resourceTypeName = resources.getResourceTypeName(resourceId);
34510bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            if (!expectedType.equals(resourceTypeName)) {
34520bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                Log.w(TAG, "Resource " + resourceId + " (" + resourceEntryName + ") is of type " +
34530bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                        resourceTypeName + " but " + expectedType + " is required.");
34540bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                return null;
34550bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            }
34560bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann
34570bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            return resourceEntryName;
34580bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann        } catch (NotFoundException e) {
34590bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            return null;
34600bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann        }
34610bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann    }
34620bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann
34634f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3464de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
3465bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3466b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "deleteInTransaction: " + uri);
3467b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
34685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
34695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
34705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
3471078f588cef389358adabc579de00747878f3c108Dave Santoro            mActiveDb.set(mContactsHelper.getWritableDatabase());
34725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
34735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
3474b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3475f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3476f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
3477508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
3478508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
347935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
34805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE:
34815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().delete(mActiveDb.get(), selection,
34825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionArgs);
34835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
34845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case SYNCSTATE_ID: {
34855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                String selectionWithId =
34865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
34875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        + (selection == null ? "" : " AND (" + selection + ")");
34885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().delete(mActiveDb.get(), selectionWithId,
34895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionArgs);
34905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
349135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
34925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE_ID: {
3493b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3494b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3495b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
34965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mProfileHelper.getSyncState().delete(mActiveDb.get(), selectionWithId,
34975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionArgs);
34985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
3499b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3500cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            case CONTACTS: {
3501cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // TODO
3502cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return 0;
3503cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3504cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3505d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
3506d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3507dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
35086bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
35096bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
35109fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP: {
35112e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
35122e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
35132e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
35145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
3515fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
35162e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
35172e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
35185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                final long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
3519dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
35202e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
35212e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
35229fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP_ID: {
35239fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                // lookup contact by id and lookup key to see if they still match the actual record
35249fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final List<String> pathSegments = uri.getPathSegments();
35259fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final String lookupKey = pathSegments.get(2);
35269fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
35279fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                setTablesAndProjectionMapForContacts(lookupQb, uri, null);
3528a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
35299fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                String[] args;
35309fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                if (selectionArgs == null) {
35319fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[2];
35329fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } else {
35339fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[selectionArgs.length + 2];
35349fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
35359fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
35369fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                args[0] = String.valueOf(contactId);
353760de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann                args[1] = Uri.encode(lookupKey);
35389fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                lookupQb.appendWhere(Contacts._ID + "=? AND " + Contacts.LOOKUP_KEY + "=?");
35395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = query(mActiveDb.get(), lookupQb, null, selection, args, null, null,
35405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        null);
35419fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                try {
35429fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    if (c.getCount() == 1) {
35439fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // contact was unmodified so go ahead and delete it
3544dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        return deleteContact(contactId, callerIsSyncAdapter);
35459fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    } else {
35469fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // row was changed (e.g. the merging might have changed), we got multiple
35479fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // rows or the supplied selection filtered the record out
35489fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        return 0;
35499fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    }
35509fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } finally {
35519fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    c.close();
35529fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
35539fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            }
35549fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
3555d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS:
3556d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS: {
35572971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
35585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS,
3559fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        new String[]{RawContacts._ID, RawContacts.CONTACT_ID},
3560e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
35612971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
35622971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
35632971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                        final long rawContactId = c.getLong(0);
3564fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        long contactId = c.getLong(1);
3565fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        numDeletes += deleteRawContact(rawContactId, contactId,
3566fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                                callerIsSyncAdapter);
35672971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
35682971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
35692971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
35702971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
35712971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
35722971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
35732971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
3574d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS_ID:
3575d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID: {
35762971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                final long rawContactId = ContentUris.parseId(uri);
35775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return deleteRawContact(rawContactId, mDbHelper.get().getContactId(rawContactId),
3578fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        callerIsSyncAdapter);
3579508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3580508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
35810c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case DATA:
35820c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case PROFILE_DATA: {
3583f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
3584944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                return deleteData(appendAccountToSelection(uri, selection), selectionArgs,
3585f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        callerIsSyncAdapter);
358620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
358720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
358848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
358948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
359048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
3591d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case POSTALS_ID:
3592d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_DATA_ID: {
3593508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
3594f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
35954da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
35964da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                return deleteData(Data._ID + "=?", mSelectionArgs1, callerIsSyncAdapter);
3597ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3598ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3599ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3600f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
36015aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteGroup(uri, ContentUris.parseId(uri), callerIsSyncAdapter);
36022971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
36032971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
36042971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case GROUPS: {
36052971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
36065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = mActiveDb.get().query(Tables.GROUPS, new String[]{Groups._ID},
3607e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
36082971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
36092971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
36105aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        numDeletes += deleteGroup(uri, c.getLong(0), callerIsSyncAdapter);
36112971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
36122971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
36132971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
36142971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
361581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (numDeletes > 0) {
3616f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
361781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
36182971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
3619508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3620508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
3621eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
362243880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3623e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                return deleteSettings(uri, appendAccountToSelection(uri, selection), selectionArgs);
3624eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3625eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
36265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case STATUS_UPDATES:
36275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_STATUS_UPDATES: {
36280a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                return deleteStatusUpdates(selection, selectionArgs);
36291f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
36301f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
36313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
36323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
36333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItems(uri, new ContentValues(), selection, selectionArgs);
36343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
36353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
36363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
36373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
36383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItems(uri, new ContentValues(),
36399b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann                        StreamItems._ID + "=?",
36403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{uri.getLastPathSegment()});
36413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
36423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
364382780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            case RAW_CONTACTS_ID_STREAM_ITEMS_ID: {
364482780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                mSyncToNetwork |= !callerIsSyncAdapter;
364582780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                String rawContactId = uri.getPathSegments().get(1);
364682780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                String streamItemId = uri.getLastPathSegment();
364782780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                return deleteStreamItems(uri, new ContentValues(),
364882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        StreamItems.RAW_CONTACT_ID + "=? AND " + StreamItems._ID + "=?",
364982780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        new String[]{rawContactId, streamItemId});
365082780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
365182780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            }
365282780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
36533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
36543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
36555d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                String streamItemId = uri.getPathSegments().get(1);
36565d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                String selectionWithId =
36575d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                        (StreamItemPhotos.STREAM_ITEM_ID + "=" + streamItemId + " ")
36585d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                                + (selection == null ? "" : " AND (" + selection + ")");
36595d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                return deleteStreamItemPhotos(uri, new ContentValues(),
36605d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                        selectionWithId, selectionArgs);
36613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
36623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
36633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
36643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
36653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
36663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
36673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItemPhotos(uri, new ContentValues(),
36683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=? AND "
36693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                                + StreamItemPhotos.STREAM_ITEM_ID + "=?",
36703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{streamItemPhotoId, streamItemId});
36713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
36723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
367381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
367481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
36753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
367681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
3677508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
36784f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
36794f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
36801c8e40c18f92722b9bec6e8ce2e345a9828efa16Dmitri Plotnikov    public int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) {
3681ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
36825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        final long groupMembershipMimetypeId = mDbHelper.get()
368394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
36845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.get().delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
368594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
368694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupId, null);
368794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
368894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        try {
3689f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (callerIsSyncAdapter) {
36905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mActiveDb.get().delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
369194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            } else {
369294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.clear();
369394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.put(Groups.DELETED, 1);
3694f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mValues.put(Groups.DIRTY, 1);
36955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mActiveDb.get().update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId,
36965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        null);
369794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            }
369894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        } finally {
36991a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
370094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
370194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
370294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
37035aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
37045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        final int count = mActiveDb.get().delete(Tables.SETTINGS, selection, selectionArgs);
37051a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        mVisibleTouched = true;
3706e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3707e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3708e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3709dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int deleteContact(long contactId, boolean callerIsSyncAdapter) {
371096b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(contactId);
37115d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
371296b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                RawContacts.CONTACT_ID + "=?", mSelectionArgs1,
371396b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                null, null, null);
3714cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        try {
3715cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            while (c.moveToNext()) {
3716cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                long rawContactId = c.getLong(0);
3717dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
3718cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3719cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } finally {
3720cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            c.close();
3721cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
3722cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
37233826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
37243826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
37255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
3726cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3727cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3728fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov    public int deleteRawContact(long rawContactId, long contactId, boolean callerIsSyncAdapter) {
37295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().invalidateAggregationExceptionCache();
37303826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
37313826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
373282780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        // Find and delete stream items associated with the raw contact.
373382780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        Cursor c = mActiveDb.get().query(Tables.STREAM_ITEMS,
373482780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                new String[]{StreamItems._ID},
373582780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                StreamItems.RAW_CONTACT_ID + "=?", new String[]{String.valueOf(rawContactId)},
373682780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                null, null, null);
373782780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        try {
373882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            while (c.moveToNext()) {
373982780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                deleteStreamItem(c.getLong(0));
374082780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            }
374182780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        } finally {
374282780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            c.close();
374382780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        }
374482780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
3745d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro        if (callerIsSyncAdapter || rawContactIsLocal(rawContactId)) {
37465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().delete(Tables.PRESENCE,
37475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
37485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            int count = mActiveDb.get().delete(Tables.RAW_CONTACTS,
37495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    RawContacts._ID + "=" + rawContactId, null);
375041f76a59a31946f6d784dacf9f13d9a4c0bbe203Dave Santoro            mAggregator.get().updateAggregateData(mTransactionContext.get(), contactId);
3751fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return count;
375233b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } else {
37535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mDbHelper.get().removeContactIfSingleton(rawContactId);
3754dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
375533b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        }
375633b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
375733b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
3758d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro    /**
3759d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro     * Returns whether the given raw contact ID is local (i.e. has no account associated with it).
3760d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro     */
3761d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro    private boolean rawContactIsLocal(long rawContactId) {
3762d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro        Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS,
3763d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                new String[] {
3764d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                        RawContacts.ACCOUNT_NAME,
3765d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                        RawContacts.ACCOUNT_TYPE,
3766d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                        RawContacts.DATA_SET
3767d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                },
3768d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                RawContacts._ID + "=?",
3769d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                new String[] {String.valueOf(rawContactId)}, null, null, null);
3770d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro        try {
3771d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            return c.moveToFirst() && c.isNull(0) && c.isNull(1) && c.isNull(2);
3772d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro        } finally {
3773d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            c.close();
3774d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro        }
3775d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro    }
3776d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro
37770a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private int deleteStatusUpdates(String selection, String[] selectionArgs) {
37789705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // delete from both tables: presence and status_updates
37799705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // TODO should account type/name be appended to the where clause?
37809705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      if (VERBOSE_LOGGING) {
37819705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          Log.v(TAG, "deleting data from status_updates for " + selection);
37829705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      }
37835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro      mActiveDb.get().delete(Tables.STATUS_UPDATES, getWhereClauseForStatusUpdatesTable(selection),
37849705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          selectionArgs);
37855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro      return mActiveDb.get().delete(Tables.PRESENCE, selection, selectionArgs);
37860a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
37870a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
37883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItems(Uri uri, ContentValues values, String selection,
37893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
37903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // First query for the stream items to be deleted, and check that they belong
37913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // to the account.
37923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
37933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemIds = enforceModifyingAccountForStreamItems(
37943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                account, selection, selectionArgs);
37953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
37963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If no security exception has been thrown, we're fine to delete.
37973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        for (long streamItemId : streamItemIds) {
37983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            deleteStreamItem(streamItemId);
37993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
38003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
38013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mVisibleTouched = true;
38023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemIds.size();
38033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
38043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
38053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItem(long streamItemId) {
38063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Note that this does not enforce the modifying account.
38073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        deleteStreamItemPhotos(streamItemId);
38085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().delete(Tables.STREAM_ITEMS, StreamItems._ID + "=?",
38093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{String.valueOf(streamItemId)});
38103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
38113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
38123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItemPhotos(Uri uri, ContentValues values, String selection,
38133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
38143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // First query for the stream item photos to be deleted, and check that they
38153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // belong to the account.
38163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
38173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItemPhotos(account, selection, selectionArgs);
38183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
38193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If no security exception has been thrown, we're fine to delete.
38205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().delete(Tables.STREAM_ITEM_PHOTOS, selection, selectionArgs);
38213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
38223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
38233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItemPhotos(long streamItemId) {
38243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Note that this does not enforce the modifying account.
38255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().delete(Tables.STREAM_ITEM_PHOTOS,
38265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                StreamItemPhotos.STREAM_ITEM_ID + "=?",
38273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{String.valueOf(streamItemId)});
38283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
38293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
3830dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int markRawContactAsDeleted(long rawContactId, boolean callerIsSyncAdapter) {
383181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = true;
383281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
3833cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.clear();
3834cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DELETED, 1);
3835cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
3836cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
3837cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
3838cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DIRTY, 1);
3839dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        return updateRawContact(rawContactId, mValues, callerIsSyncAdapter);
3840cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3841cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
38424f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3843de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
3844de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            String[] selectionArgs) {
3845bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3846b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "updateInTransaction: " + uri);
3847b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3848b5a4add17815167d20a90645779df34cdf45280dFred Quintana
38495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
38505d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
3851078f588cef389358adabc579de00747878f3c108Dave Santoro            mActiveDb.set(mContactsHelper.getWritableDatabase());
38525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
38535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
385435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
385500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
385600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
3857b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (match == SYNCSTATE_ID && selection == null) {
3858b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long rowId = ContentUris.parseId(uri);
38591129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Object data = values.get(ContactsContract.SyncState.DATA);
38605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mTransactionContext.get().syncStateUpdated(rowId, data);
3861b5a4add17815167d20a90645779df34cdf45280dFred Quintana            return 1;
3862b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3863b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3864f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3865f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
386600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
386735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
38685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE:
38695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().update(mActiveDb.get(), values,
3870b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        appendAccountToSelection(uri, selection), selectionArgs);
3871b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3872b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID: {
3873b5a4add17815167d20a90645779df34cdf45280dFred Quintana                selection = appendAccountToSelection(uri, selection);
3874b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3875b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3876b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
38775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().update(mActiveDb.get(), values,
38785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionWithId, selectionArgs);
38795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
38805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
38815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE_ID: {
38825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                selection = appendAccountToSelection(uri, selection);
38835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                String selectionWithId =
38845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
38855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        + (selection == null ? "" : " AND (" + selection + ")");
38865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mProfileHelper.getSyncState().update(mActiveDb.get(), values,
3887b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        selectionWithId, selectionArgs);
3888b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
388935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3890d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case CONTACTS:
3891d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE: {
3892dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(values, selection, selectionArgs, callerIsSyncAdapter);
389300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
389400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
389500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3896d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
3897dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(ContentUris.parseId(uri), values, callerIsSyncAdapter);
3898c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
3899c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
3900c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
39012e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
39022e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
39032e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
39042e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
39052e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
39065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
3907fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
39082e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
39092e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
39105d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                final long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
3911dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(contactId, values, callerIsSyncAdapter);
39122e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                break;
39132e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
39142e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
3915d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS_DATA:
3916d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID_DATA: {
3917d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                int segment = match == RAW_CONTACTS_DATA ? 1 : 2;
3918d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                final String rawContactId = uri.getPathSegments().get(segment);
39197d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
39207d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                    + (selection == null ? "" : " AND " + selection);
39217d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
39227d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter);
39237d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
39247d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                break;
39257d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            }
39267d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
39270c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case DATA:
39280c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case PROFILE_DATA: {
3929944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                count = updateData(uri, values, appendAccountToSelection(uri, selection),
3930f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
393181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3932f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
393381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
393420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
393520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3936c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
393748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
393848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
393948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
394048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
3941f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
394281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3943f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
394481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
394500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
394600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
39477e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
39485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case RAW_CONTACTS:
39495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_RAW_CONTACTS: {
39505ac5c70d1165309302ebcc931f51723e37d31e0bJeff Sharkey                selection = appendAccountToSelection(uri, selection);
3951dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateRawContacts(values, selection, selectionArgs, callerIsSyncAdapter);
39527e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
39537e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
39547e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
39555ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
395633b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
39574529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                if (selection != null) {
39584da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
39594da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=?"
3960dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                                    + " AND(" + selection + ")", selectionArgs,
3961dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
39624529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                } else {
39634da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(rawContactId);
3964dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    count = updateRawContacts(values, RawContacts._ID + "=?", mSelectionArgs1,
3965dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
39664529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                }
39677e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
39687e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
39697e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
3970ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
39715aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, appendAccountToSelection(uri, selection),
3972f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
397381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3974f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
397581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3976ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3977ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3978ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3979ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3980ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
39814da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(groupId));
39824da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                String selectionWithId = Groups._ID + "=? "
398373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                        + (selection == null ? "" : " AND " + selection);
39845aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, selectionWithId, selectionArgs,
39855aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        callerIsSyncAdapter);
398681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3987f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
398881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3989ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3990ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3991ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3992127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
39935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                count = updateAggregationException(mActiveDb.get(), values);
3994b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
3995b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
3996b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
3997eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
3998e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                count = updateSettings(uri, values, appendAccountToSelection(uri, selection),
3999e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        selectionArgs);
400043880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
4001eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
4002eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
4003eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
40045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case STATUS_UPDATES:
40055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_STATUS_UPDATES: {
40069705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                count = updateStatusUpdate(uri, values, selection, selectionArgs);
40079705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                break;
40089705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            }
40099705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
40103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
40113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItems(uri, values, selection, selectionArgs);
40123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
40133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
40143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
40153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
40169b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann                count = updateStreamItems(uri, values, StreamItems._ID + "=?",
40173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{uri.getLastPathSegment()});
40183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
40193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
40203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
402182780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            case RAW_CONTACTS_ID_STREAM_ITEMS_ID: {
402282780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                String rawContactId = uri.getPathSegments().get(1);
402382780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                String streamItemId = uri.getLastPathSegment();
402482780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                count = updateStreamItems(uri, values,
402582780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        StreamItems.RAW_CONTACT_ID + "=? AND " + StreamItems._ID + "=?",
402682780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        new String[]{rawContactId, streamItemId});
402782780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                break;
402882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            }
402982780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
40303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
40313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values, selection, selectionArgs);
40323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
40333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
40343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
40353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
40363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
40373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values,
40383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotos.STREAM_ITEM_ID + "=?", new String[]{streamItemId});
40393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
40403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
40413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
40423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
40433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
40443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
40453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values,
40463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=? AND " +
40473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                                StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=?",
40483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{streamItemPhotoId, streamItemId});
40493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
40503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
40513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
405272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            case DIRECTORIES: {
4053bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                mContactDirectoryManager.scanPackagesByUid(Binder.getCallingUid());
405472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                count = 1;
4055d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
4056d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4057d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
405846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            case DATA_USAGE_FEEDBACK_ID: {
405946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (handleDataUsageFeedback(uri)) {
406046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    count = 1;
406146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                } else {
406246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    count = 0;
406346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
406446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                break;
406546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
406646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
406781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
406881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
4069f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
407081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
407100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
407200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
407300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
40744f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
40754f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
40769705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private int updateStatusUpdate(Uri uri, ContentValues values, String selection,
40779705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        String[] selectionArgs) {
40789705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // update status_updates table, if status is provided
40799705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO should account type/name be appended to the where clause?
40809705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        int updateCount = 0;
40819705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContentValues settableValues = getSettableColumnsForStatusUpdatesTable(values);
40829705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
40835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro          updateCount = mActiveDb.get().update(Tables.STATUS_UPDATES,
40849705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    settableValues,
40859705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    getWhereClauseForStatusUpdatesTable(selection),
40869705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selectionArgs);
40879705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
40889705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
40899705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // now update the Presence table
40909705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        settableValues = getSettableColumnsForPresenceTable(values);
40919705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
40925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro          updateCount = mActiveDb.get().update(Tables.PRESENCE, settableValues,
40939705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selection, selectionArgs);
40949705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
40959705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO updateCount is not entirely a valid count of updated rows because 2 tables could
40969705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // potentially get updated in this method.
40979705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return updateCount;
40989705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
40999705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
41003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int updateStreamItems(Uri uri, ContentValues values, String selection,
41013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
41023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Stream items can't be moved to a new raw contact.
41033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        values.remove(StreamItems.RAW_CONTACT_ID);
41043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
41053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check that the stream items being updated belong to the account.
41063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
41073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItems(account, selection, selectionArgs);
41083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
41096802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to update accounts params - they don't exist in the stream items table.
41106802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_NAME);
41116802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_TYPE);
41126802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
41133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If there's been no exception, the update should be fine.
41145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().update(Tables.STREAM_ITEMS, values, selection, selectionArgs);
41153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
41163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
41173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int updateStreamItemPhotos(Uri uri, ContentValues values, String selection,
41183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
41193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Stream item photos can't be moved to a new stream item.
41203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        values.remove(StreamItemPhotos.STREAM_ITEM_ID);
41213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
41223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check that the stream item photos being updated belong to the account.
41233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
41243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItemPhotos(account, selection, selectionArgs);
41253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
41266802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to update accounts params - they don't exist in the stream item
41276802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // photos table.
41286802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_NAME);
41296802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_TYPE);
41306802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
41316802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Process the photo (since we're updating, it's valid for the photo to not be present).
41326802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (processStreamItemPhoto(values, true)) {
41336802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // If there's been no exception, the update should be fine.
41345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return mActiveDb.get().update(Tables.STREAM_ITEM_PHOTOS, values, selection,
41355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    selectionArgs);
41366802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
41376802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        return 0;
41383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
41393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
41409705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    /**
41419705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     * Build a where clause to select the rows to be updated in status_updates table.
41429705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     */
41439705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private String getWhereClauseForStatusUpdatesTable(String selection) {
41449705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.setLength(0);
41459705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE);
41469705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(selection);
41479705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(")");
41489705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mSb.toString();
41499705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
41509705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
41519705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForStatusUpdatesTable(ContentValues values) {
41529705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
41539705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS, values,
41549705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS);
41559705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_TIMESTAMP, values,
41569705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_TIMESTAMP);
41579705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_RES_PACKAGE, values,
41589705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_RES_PACKAGE);
41599705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_LABEL, values,
41609705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_LABEL);
41619705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_ICON, values,
41629705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_ICON);
41639705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
41649705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
41659705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
41669705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForPresenceTable(ContentValues values) {
41679705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
41689705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.PRESENCE, values,
41699705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.PRESENCE);
4170aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.CHAT_CAPABILITY, values,
4171aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                StatusUpdates.CHAT_CAPABILITY);
41729705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
41739705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
41749705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
41755aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int updateGroups(Uri uri, ContentValues values, String selectionWithId,
4176f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
417773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
4178ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
4179ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
418073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        ContentValues updatedValues;
4181f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter && !values.containsKey(Groups.DIRTY)) {
418273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = mValues;
418373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.clear();
418473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.putAll(values);
418573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.put(Groups.DIRTY, 1);
418673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } else {
418773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = values;
418873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
418973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
41905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int count = mActiveDb.get().update(Tables.GROUPS, updatedValues, selectionWithId,
41915d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                selectionArgs);
41921a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) {
41931a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
419494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
419543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
419643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        // TODO: This will not work for groups that have a data set specified, since the content
419743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        // resolver will not be able to request a sync for the right source (unless it is updated
419843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        // to key off account with data set).
41996ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        if (updatedValues.containsKey(Groups.SHOULD_SYNC)
42001129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                && updatedValues.getAsInteger(Groups.SHOULD_SYNC) != 0) {
42015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            Cursor c = mActiveDb.get().query(Tables.GROUPS, new String[]{Groups.ACCOUNT_NAME,
4202e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    Groups.ACCOUNT_TYPE}, selectionWithId, selectionArgs, null,
42036ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    null, null);
42046ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountName;
42056ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountType;
42066ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            try {
42076ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                while (c.moveToNext()) {
42086ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountName = c.getString(0);
42096ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountType = c.getString(1);
421024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
42116ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        Account account = new Account(accountName, accountType);
4212ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        ContentResolver.requestSync(account, ContactsContract.AUTHORITY,
42136ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                                new Bundle());
42146ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        break;
42156ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    }
42166ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                }
42176ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            } finally {
42186ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                c.close();
42196ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            }
42206ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        }
422194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        return count;
422294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
422394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
4224b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private int updateSettings(Uri uri, ContentValues values, String selection,
4225b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            String[] selectionArgs) {
42265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        final int count = mActiveDb.get().update(Tables.SETTINGS, values, selection, selectionArgs);
42271a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
42281a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
4229e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
4230e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
4231e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
4232e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
4233dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContacts(ContentValues values, String selection, String[] selectionArgs,
4234dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
42354529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (values.containsKey(RawContacts.CONTACT_ID)) {
42364529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            throw new IllegalArgumentException(RawContacts.CONTACT_ID + " should not be included " +
42374529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    "in content values. Contact IDs are assigned automatically");
42384529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
423973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
424097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
424197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
424297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0");
424397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
424497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
42454529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = 0;
42465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor cursor = mActiveDb.get().query(Views.RAW_CONTACTS,
424751bf5ea9531b9da72caff607dbdf35fd6f61cbe2Jeff Sharkey                new String[] { RawContacts._ID }, selection,
42484529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                selectionArgs, null, null, null);
42494529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        try {
42504529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            while (cursor.moveToNext()) {
42514529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                long rawContactId = cursor.getLong(0);
4252dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContact(rawContactId, values, callerIsSyncAdapter);
42534529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count++;
42544529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            }
42554529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        } finally {
42564529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            cursor.close();
42574529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
42584529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
42594529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return count;
42604529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    }
42614529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
4262dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContact(long rawContactId, ContentValues values,
4263dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
426496b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        final String selection = RawContacts._ID + " = ?";
426596b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(rawContactId);
426619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final boolean requestUndoDelete = (values.containsKey(RawContacts.DELETED)
426719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                && values.getAsInteger(RawContacts.DELETED) == 0);
426819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int previousDeleted = 0;
4269ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType = null;
4270ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName = null;
427143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String dataSet = null;
427219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        if (requestUndoDelete) {
42735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            Cursor cursor = mActiveDb.get().query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS,
42745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    selection, mSelectionArgs1, null, null, null);
427519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            try {
427619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                if (cursor.moveToFirst()) {
427719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    previousDeleted = cursor.getInt(RawContactsQuery.DELETED);
4278ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountType = cursor.getString(RawContactsQuery.ACCOUNT_TYPE);
4279ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountName = cursor.getString(RawContactsQuery.ACCOUNT_NAME);
428043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    dataSet = cursor.getString(RawContactsQuery.DATA_SET);
428119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                }
428219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            } finally {
428319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                cursor.close();
428419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
428519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
428619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT);
428719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        }
4288f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
42895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int count = mActiveDb.get().update(Tables.RAW_CONTACTS, values, selection, mSelectionArgs1);
42905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count != 0) {
4291f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            if (values.containsKey(RawContacts.AGGREGATION_MODE)) {
4292f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                int aggregationMode = values.getAsInteger(RawContacts.AGGREGATION_MODE);
4293f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov
4294f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // As per ContactsContract documentation, changing aggregation mode
4295f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // to DEFAULT should not trigger aggregation
4296f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                if (aggregationMode != RawContacts.AGGREGATION_MODE_DEFAULT) {
42975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mAggregator.get().markForAggregation(rawContactId, aggregationMode, false);
4298f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                }
4299f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            }
4300433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            if (values.containsKey(RawContacts.STARRED)) {
4301dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter) {
4302dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
4303dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            values.getAsLong(RawContacts.STARRED) != 0);
4304dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
43055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mAggregator.get().updateStarred(rawContactId);
4306dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
4307dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // if this raw contact is being associated with an account, then update the
4308dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // favorites group membership based on whether or not this contact is starred.
4309dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // If it is starred, add a group membership, if one doesn't already exist
4310dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // otherwise delete any matching group memberships.
4311dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
43125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    boolean starred = 0 != DatabaseUtils.longForQuery(mActiveDb.get(),
4313dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            SELECTION_STARRED_FROM_RAW_CONTACTS,
4314dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            new String[]{Long.toString(rawContactId)});
4315dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId, starred);
4316dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
4317dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
4318dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
4319dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // if this raw contact is being associated with an account, then add a
4320dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // group membership to the group marked as AutoAdd, if any.
4321dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
4322dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                addAutoAddMembership(rawContactId);
4323433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            }
4324dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
4325285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (values.containsKey(RawContacts.SOURCE_ID)) {
43265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mAggregator.get().updateLookupKeyForRawContact(mActiveDb.get(), rawContactId);
4327285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
4328f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            if (values.containsKey(RawContacts.NAME_VERIFIED)) {
4329f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
4330f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // If setting NAME_VERIFIED for this raw contact, reset it for all
4331f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // other raw contacts in the same aggregate
4332f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                if (values.getAsInteger(RawContacts.NAME_VERIFIED) != 0) {
43335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mDbHelper.get().resetNameVerifiedForOtherRawContacts(rawContactId);
4334f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                }
43355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mAggregator.get().updateDisplayNameForRawContact(mActiveDb.get(), rawContactId);
4336f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            }
433719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            if (requestUndoDelete && previousDeleted == 1) {
43385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mTransactionContext.get().rawContactInserted(rawContactId,
433943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        new AccountWithDataSet(accountName, accountType, dataSet));
434019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
43415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
43425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return count;
434333b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
434433b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
4345321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(Uri uri, ContentValues values, String selection,
4346f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
434720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
434820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
434920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
43505ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
435120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
435220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
435320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
435420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
435520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
43565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.get().getPackageId(packageName));
435720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
435820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
435997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
436097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
436197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    Data.IS_READ_ONLY + "=0");
436297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
436397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
4364653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        int count = 0;
436520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
4366653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
4367653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // so we don't need to worry about updating data we don't have permission to read.
43685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = queryLocal(uri,
4369f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                DataRowHandler.DataUpdateQuery.COLUMNS,
43705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                selection, selectionArgs, null, -1 /* directory ID */);
4371653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        try {
4372653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            while(c.moveToNext()) {
4373f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count += updateData(mValues, c, callerIsSyncAdapter);
437420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
4375653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        } finally {
4376653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            c.close();
437720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
437820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
4379653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return count;
438020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
438120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
4382f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
4383653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        if (values.size() == 0) {
4384653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return 0;
4385321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
4386653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
4387f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        final String mimeType = c.getString(DataRowHandler.DataUpdateQuery.MIMETYPE);
4388a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
4389f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        boolean updated =
43905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                rowHandler.update(mActiveDb.get(), mTransactionContext.get(), values, c,
43915d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        callerIsSyncAdapter);
4392f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
4393f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
4394a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
4395f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        return updated ? 1 : 0;
4396321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    }
4397321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
43988c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(ContentValues values, String selection,
4399dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
44008c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        int count = 0;
44015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor cursor = mActiveDb.get().query(Views.CONTACTS,
44025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new String[] { Contacts._ID }, selection, selectionArgs, null, null, null);
44038c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        try {
44048c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            while (cursor.moveToNext()) {
44058c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                long contactId = cursor.getLong(0);
440624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
4407dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateContactOptions(contactId, values, callerIsSyncAdapter);
44088c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count++;
44098c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            }
44108c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        } finally {
44118c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            cursor.close();
44128c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        }
44138c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
44148c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return count;
44158c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
44168c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
4417dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateContactOptions(long contactId, ContentValues values,
4418dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
4419d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
44208c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
4421b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
4422d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
4423b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
4424d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
4425b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
4426d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
4427b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
4428d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
4429b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
4430d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.STARRED);
4431d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
4432d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
44338c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.size() == 0) {
4434d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
4435d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
4436d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
44378c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.containsKey(RawContacts.STARRED)) {
4438c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            // Mark dirty when changing starred to trigger sync
44398c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            mValues.put(RawContacts.DIRTY, 1);
4440c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey        }
4441c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey
44424da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(contactId);
44435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.get().update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=?"
444497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                + " AND " + RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0", mSelectionArgs1);
44458c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
4446dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (mValues.containsKey(RawContacts.STARRED) && !callerIsSyncAdapter) {
44475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            Cursor cursor = mActiveDb.get().query(Views.RAW_CONTACTS,
4448dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[] { RawContacts._ID }, RawContacts.CONTACT_ID + "=?",
4449dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    mSelectionArgs1, null, null, null);
4450dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            try {
4451dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                while (cursor.moveToNext()) {
4452dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    long rawContactId = cursor.getLong(0);
4453dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
4454dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            mValues.getAsLong(RawContacts.STARRED) != 0);
4455dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
4456dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } finally {
4457dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                cursor.close();
4458dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
4459dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
4460dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
44618c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // Copy changeable values to prevent automatically managed fields from
44628c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // being explicitly updated by clients.
44638c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
4464b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
44658c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
4466b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
44678c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
4468b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
44698c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
4470b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
44718c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
4472b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
44738c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.STARRED);
44748c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
44755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int rslt = mActiveDb.get().update(Tables.CONTACTS, mValues, Contacts._ID + "=?",
44765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mSelectionArgs1);
44776e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori
44789b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        if (values.containsKey(Contacts.LAST_TIME_CONTACTED) &&
44799b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori                !values.containsKey(Contacts.TIMES_CONTACTED)) {
44805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
44815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
44829b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        }
44839b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        return rslt;
4484f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
4485d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
4486127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
4487127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
44880c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId1 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID1);
44890c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId2 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID2);
449080c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
4491ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId1;
4492ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId2;
44930c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (rcId1 < rcId2) {
44940c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId1;
44950c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId2;
44960c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
44970c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId1;
44980c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId2;
4499b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
4500127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
45010c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
45024da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[0] = String.valueOf(rawContactId1);
45034da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[1] = String.valueOf(rawContactId2);
45040c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.delete(Tables.AGGREGATION_EXCEPTIONS,
45054da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    AggregationExceptions.RAW_CONTACT_ID1 + "=? AND "
45064da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    + AggregationExceptions.RAW_CONTACT_ID2 + "=?", mSelectionArgs2);
45070c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
45086bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            ContentValues exceptionValues = new ContentValues(3);
45096bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
45100c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
45110c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
45120c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
45130c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    exceptionValues);
4514127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
4515127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
45165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().invalidateAggregationExceptionCache();
45175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().markForAggregation(rawContactId1,
451869cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
45195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().markForAggregation(rawContactId2,
452069cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
4521dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov
45225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().aggregateContact(mTransactionContext.get(), db, rawContactId1);
45235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().aggregateContact(mTransactionContext.get(), db, rawContactId2);
4524127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
4525127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
4526127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
4527127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
4528b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
4529b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
453070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    public void onAccountsUpdated(Account[] accounts) {
4531bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
45323826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
45333826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
4534bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected boolean updateAccountsInBackground(Account[] accounts) {
4535f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao        // TODO : Check the unit test.
4536e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov        boolean accountsChanged = false;
45375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
45385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.set(db);
45395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        db.beginTransaction();
45405dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro
45415dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro        // WARNING: This method can be run in either contacts mode or profile mode.  It is
45425dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro        // absolutely imperative that no calls be made inside the following try block that can
45435dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro        // interact with the contacts DB.  Otherwise it is quite possible for a deadlock to occur.
454470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        try {
454543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            Set<AccountWithDataSet> existingAccountsWithDataSets =
454643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    findValidAccountsWithDataSets(Tables.ACCOUNTS);
4547743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov
454843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // Add a row to the ACCOUNTS table (with no data set) for each new account.
4549743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            for (Account account : accounts) {
455043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                AccountWithDataSet accountWithDataSet = new AccountWithDataSet(
455143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        account.name, account.type, null);
455243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                if (!existingAccountsWithDataSets.contains(accountWithDataSet)) {
4553e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    accountsChanged = true;
455443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
455543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    // Add an account entry with an empty data set to match the account.
45565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME
455743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            + ", " + RawContacts.ACCOUNT_TYPE + ", " + RawContacts.DATA_SET
455843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            + ") VALUES (?, ?, ?)",
455943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            new String[] {
456043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getAccountName(),
456143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getAccountType(),
456243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getDataSet()
456343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            });
4564743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                }
4565743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            }
456648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
456743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // Check each of the existing sub-accounts against the account list.  If the owning
456843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // account no longer exists, the sub-account and all its data should be deleted.
456943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            List<AccountWithDataSet> accountsWithDataSetsToDelete =
457043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    new ArrayList<AccountWithDataSet>();
457143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            List<Account> accountList = Arrays.asList(accounts);
457243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            for (AccountWithDataSet accountWithDataSet : existingAccountsWithDataSets) {
457343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                Account owningAccount = new Account(
457443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        accountWithDataSet.getAccountName(), accountWithDataSet.getAccountType());
457543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                if (!accountList.contains(owningAccount)) {
457643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    accountsWithDataSetsToDelete.add(accountWithDataSet);
457743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                }
457870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
457970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
458043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            if (!accountsWithDataSetsToDelete.isEmpty()) {
4581e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                accountsChanged = true;
458243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                for (AccountWithDataSet accountWithDataSet : accountsWithDataSetsToDelete) {
458343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    Log.d(TAG, "removing data for removed account " + accountWithDataSet);
458443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    String[] accountParams = new String[] {
458543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            accountWithDataSet.getAccountName(),
458643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            accountWithDataSet.getAccountType()
458743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    };
458843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    String[] accountWithDataSetParams = accountWithDataSet.getDataSet() == null
458943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            ? accountParams
459043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            : new String[] {
459143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getAccountName(),
459243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getAccountType(),
459343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getDataSet()
459443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            };
459543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    String groupsDataSetClause = " AND " + Groups.DATA_SET
459643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            + (accountWithDataSet.getDataSet() == null ? " IS NULL" : " = ?");
459743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    String rawContactsDataSetClause = " AND " + RawContacts.DATA_SET
459843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            + (accountWithDataSet.getDataSet() == null ? " IS NULL" : " = ?");
4599f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                    String settingsDataSetClause = " AND " + Settings.DATA_SET
4600f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                            + (accountWithDataSet.getDataSet() == null ? " IS NULL" : " = ?");
460143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
46025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4603e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.GROUPS +
4604e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Groups.ACCOUNT_NAME + " = ?" +
460543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    " AND " + Groups.ACCOUNT_TYPE + " = ?" +
460643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    groupsDataSetClause, accountWithDataSetParams);
46075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4608e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.PRESENCE +
4609e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (" +
4610e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    "SELECT " + RawContacts._ID +
4611e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " FROM " + Tables.RAW_CONTACTS +
4612e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
461343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    " AND " + RawContacts.ACCOUNT_TYPE + " = ?" +
461443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    rawContactsDataSetClause + ")", accountWithDataSetParams);
46155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4616c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            "DELETE FROM " + Tables.STREAM_ITEM_PHOTOS +
4617c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            " WHERE " + StreamItemPhotos.STREAM_ITEM_ID + " IN (" +
4618c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    "SELECT " + StreamItems._ID +
4619c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    " FROM " + Tables.STREAM_ITEMS +
4620c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    " WHERE " + StreamItems.RAW_CONTACT_ID + " IN (" +
4621c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                            "SELECT " + RawContacts._ID +
4622c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                            " FROM " + Tables.RAW_CONTACTS +
4623c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                            " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
4624c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                            " AND " + RawContacts.ACCOUNT_TYPE + " = ?" +
4625c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                            rawContactsDataSetClause + "))",
4626c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            accountWithDataSetParams);
4627c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                    db.execSQL(
4628c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            "DELETE FROM " + Tables.STREAM_ITEMS +
4629c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            " WHERE " + StreamItems.RAW_CONTACT_ID + " IN (" +
4630c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    "SELECT " + RawContacts._ID +
4631c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    " FROM " + Tables.RAW_CONTACTS +
4632c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
4633c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    " AND " + RawContacts.ACCOUNT_TYPE + " = ?" +
4634c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    rawContactsDataSetClause + ")",
4635c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            accountWithDataSetParams);
4636c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                    db.execSQL(
4637e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.RAW_CONTACTS +
4638e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
463943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            " AND " + RawContacts.ACCOUNT_TYPE + " = ?" +
464043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            rawContactsDataSetClause, accountWithDataSetParams);
46415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4642e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.SETTINGS +
4643e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Settings.ACCOUNT_NAME + " = ?" +
4644f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                            " AND " + Settings.ACCOUNT_TYPE + " = ?" +
4645f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                            settingsDataSetClause, accountWithDataSetParams);
46465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4647e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.ACCOUNTS +
4648e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + "=?" +
464943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            " AND " + RawContacts.ACCOUNT_TYPE + "=?" +
465043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            rawContactsDataSetClause, accountWithDataSetParams);
46515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4652d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            "DELETE FROM " + Tables.DIRECTORIES +
4653d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            " WHERE " + Directory.ACCOUNT_NAME + "=?" +
465443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            " AND " + Directory.ACCOUNT_TYPE + "=?", accountParams);
46554458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    resetDirectoryCache();
4656e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                }
4657e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov
465833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // Find all aggregated contacts that used to contain the raw contacts
465933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // we have just deleted and see if they are still referencing the deleted
4660e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                // names or photos.  If so, fix up those contacts.
466133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                HashSet<Long> orphanContactIds = Sets.newHashSet();
46625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor cursor = db.rawQuery("SELECT " + Contacts._ID +
466333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " FROM " + Tables.CONTACTS +
466433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " WHERE (" + Contacts.NAME_RAW_CONTACT_ID + " NOT NULL AND " +
466569cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                Contacts.NAME_RAW_CONTACT_ID + " NOT IN " +
466669cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + RawContacts._ID +
466769cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.RAW_CONTACTS + "))" +
466833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " OR (" + Contacts.PHOTO_ID + " NOT NULL AND " +
466933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                                Contacts.PHOTO_ID + " NOT IN " +
467069cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + Data._ID +
467169cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.DATA + "))", null);
467233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                try {
467333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    while (cursor.moveToNext()) {
467433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        orphanContactIds.add(cursor.getLong(0));
467533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    }
467633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                } finally {
467733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    cursor.close();
467833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
467933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
468033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                for (Long contactId : orphanContactIds) {
46815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mAggregator.get().updateAggregateData(mTransactionContext.get(), contactId);
468233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
46835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mDbHelper.get().updateAllVisible();
46845dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro
46855dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro                // Don't bother updating the search index if we're in profile mode - there is no
46865dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro                // search index for the profile DB, and updating it for the contacts DB in this case
46875dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro                // makes no sense and risks a deadlock.
46885dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro                if (!inProfileMode()) {
46895dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro                    updateSearchIndexInTransaction();
46905dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro                }
469133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            }
469233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
469343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // Now that we've done the account-based additions and subtractions from the Accounts
469443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // table, check for raw contacts that have been added with a data set and add Accounts
469543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // entries for those if necessary.
469643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            existingAccountsWithDataSets = findValidAccountsWithDataSets(Tables.ACCOUNTS);
469743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            Set<AccountWithDataSet> rawContactAccountsWithDataSets =
469843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    findValidAccountsWithDataSets(Tables.RAW_CONTACTS);
469943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            rawContactAccountsWithDataSets.removeAll(existingAccountsWithDataSets);
470043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
470143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // Any remaining raw contact sub-accounts need to be added to the Accounts table.
470243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            for (AccountWithDataSet accountWithDataSet : rawContactAccountsWithDataSets) {
470343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                accountsChanged = true;
470443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
470543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                // Add an account entry to match the raw contact.
47065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                db.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME
470743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + ", " + RawContacts.ACCOUNT_TYPE + ", " + RawContacts.DATA_SET
470843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + ") VALUES (?, ?, ?)",
470943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        new String[] {
471043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                accountWithDataSet.getAccountName(),
471143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                accountWithDataSet.getAccountType(),
471243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                accountWithDataSet.getDataSet()
471343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        });
471443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
471543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
4716e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            if (accountsChanged) {
471743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                // TODO: Should sync state take data set into consideration?
47185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mDbHelper.get().getSyncState().onAccountsChanged(db, accounts);
4719e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            }
47205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db.setTransactionSuccessful();
472170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } finally {
47225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db.endTransaction();
472370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
472473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.clear();
47253826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
47263826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (accountsChanged) {
47273826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateContactsAccountCount(accounts);
47283826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
47293826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
47303826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
4731afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov        return accountsChanged;
473270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
4733619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
47343826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateContactsAccountCount(Account[] accounts) {
47353826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        int count = 0;
47363826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        for (Account account : accounts) {
47373826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            if (isContactsAccount(account)) {
47383826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                count++;
47393826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            }
47403826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
47413826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mContactsAccountCount = count;
47423826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
47433826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
47443826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    protected boolean isContactsAccount(Account account) {
47453826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        final IContentService cs = ContentResolver.getContentService();
47463826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        try {
47473826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return cs.getIsSyncable(account, ContactsContract.AUTHORITY) > 0;
47483826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } catch (RemoteException e) {
47493826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            Log.e(TAG, "Cannot obtain sync flag for account: " + account, e);
47503826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return false;
47513826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
47523826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
47533826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
475472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void onPackageChanged(String packageName) {
4755bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_DIRECTORIES, packageName);
4756d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4757d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4758619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
475943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * Finds all distinct account types and data sets present in the specified table.
4760627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     */
476143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    private Set<AccountWithDataSet> findValidAccountsWithDataSets(String table) {
476243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        Set<AccountWithDataSet> accountsWithDataSets = new HashSet<AccountWithDataSet>();
47635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = mActiveDb.get().rawQuery(
476443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                "SELECT DISTINCT " + RawContacts.ACCOUNT_NAME + "," + RawContacts.ACCOUNT_TYPE +
476543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                "," + RawContacts.DATA_SET +
476643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                " FROM " + table, null);
4767627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
4768627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            while (c.moveToNext()) {
476991abbc9f691297594262d1f2d79acb744a66712cDave Santoro                if (!c.isNull(0) && !c.isNull(1)) {
477043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    accountsWithDataSets.add(
477143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            new AccountWithDataSet(c.getString(0), c.getString(1), c.getString(2)));
4772627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
4773627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
4774627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } finally {
4775627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            c.close();
4776627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
477743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        return accountsWithDataSets;
4778627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
4779627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
47804f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
47814f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
47824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
478315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
478415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mReadAccessLatch);
478515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
478636612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        // Enforce stream items access check if applicable.
478736612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        enforceSocialStreamReadPermission(uri);
478836612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
47895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Query the profile DB if appropriate.
47905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDb(uri)) {
47915d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToProfileMode();
47925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return mProfileProvider.query(uri, projection, selection, selectionArgs, sortOrder);
47935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
47945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
47955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Otherwise proceed with a normal query against the contacts DB.
47965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        switchToContactMode();
47975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.set(mContactsHelper.getReadableDatabase());
4798d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String directory = getQueryParameter(uri, ContactsContract.DIRECTORY_PARAM_KEY);
4799385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directory == null) {
4800b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return addSnippetExtrasToCursor(uri,
48015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder, -1));
4802385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directory.equals("0")) {
4803b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return addSnippetExtrasToCursor(uri,
48043716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder,
48055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            Directory.DEFAULT));
4806d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        } else if (directory.equals("1")) {
4807b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return addSnippetExtrasToCursor(uri,
48083716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder,
48095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            Directory.LOCAL_INVISIBLE));
4810d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4811d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4812d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        DirectoryInfo directoryInfo = getDirectoryAuthority(directory);
4813d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo == null) {
4814a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            Log.e(TAG, "Invalid directory ID: " + uri);
4815a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            return null;
4816d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4817d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4818d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Builder builder = new Uri.Builder();
4819d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.scheme(ContentResolver.SCHEME_CONTENT);
4820d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.authority(directoryInfo.authority);
4821d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.encodedPath(uri.getEncodedPath());
4822d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountName != null) {
4823d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_NAME, directoryInfo.accountName);
4824d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4825d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountType != null) {
4826d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, directoryInfo.accountType);
4827d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
48282e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
48292e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limit = getLimit(uri);
48302e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        if (limit != null) {
48312e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov            builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, limit);
48322e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        }
48332e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
4834d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Uri directoryUri = builder.build();
483509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
483609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        if (projection == null) {
483709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            projection = getDefaultProjection(uri);
483809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
483909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
4840332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        Cursor cursor = getContext().getContentResolver().query(directoryUri, projection, selection,
4841d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                selectionArgs, sortOrder);
48426ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
48436ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (cursor == null) {
48446ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return null;
48456ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
48466ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
4847547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        CrossProcessCursor crossProcessCursor = getCrossProcessCursor(cursor);
4848547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        if (crossProcessCursor != null) {
4849b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return addSnippetExtrasToCursor(uri, cursor);
4850547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        } else {
4851b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return matrixCursorFromCursor(addSnippetExtrasToCursor(uri, cursor));
4852547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        }
48533716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    }
48543716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
4855b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private Cursor addSnippetExtrasToCursor(Uri uri, Cursor cursor) {
4856547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro
4857547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        // If the cursor doesn't contain a snippet column, don't bother wrapping it.
4858547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        if (cursor.getColumnIndex(SearchSnippetColumns.SNIPPET) < 0) {
4859b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return cursor;
4860547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        }
4861547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro
48623716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // Parse out snippet arguments for use when snippets are retrieved from the cursor.
48633716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String[] args = null;
48643716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String snippetArgs =
48653716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                getQueryParameter(uri, SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY);
48663716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        if (snippetArgs != null) {
48673716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            args = snippetArgs.split(",");
48683716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        }
48693716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
48703716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String query = uri.getLastPathSegment();
48713716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String startMatch = args != null && args.length > 0 ? args[0]
48723716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_START_MATCH;
48733716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String endMatch = args != null && args.length > 1 ? args[1]
48743716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_END_MATCH;
48753716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String ellipsis = args != null && args.length > 2 ? args[2]
48763716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_ELLIPSIS;
48773716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        int maxTokens = args != null && args.length > 3 ? Integer.parseInt(args[3])
48783716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_MAX_TOKENS;
48793716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
4880b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        // Snippet data is needed for the snippeting on the client side, so store it in the cursor
4881b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        if (cursor instanceof AbstractCursor && deferredSnippetingRequested(uri)){
4882b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            Bundle oldExtras = cursor.getExtras();
4883b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            Bundle extras = new Bundle();
4884b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            if (oldExtras != null) {
4885b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                extras.putAll(oldExtras);
4886b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            }
4887b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            extras.putString(ContactsContract.DEFERRED_SNIPPETING_QUERY, query);
4888b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
4889b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            ((AbstractCursor) cursor).setExtras(extras);
48905517770250b3afa4fd88b6869c3244680821d222Dave Santoro        }
4891b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        return cursor;
4892b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    }
4893b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
4894b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private Cursor addDeferredSnippetingExtra(Cursor cursor) {
4895b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        if (cursor instanceof AbstractCursor){
4896b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            Bundle oldExtras = cursor.getExtras();
4897b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            Bundle extras = new Bundle();
4898b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            if (oldExtras != null) {
4899b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                extras.putAll(oldExtras);
4900b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            }
4901b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            extras.putBoolean(ContactsContract.DEFERRED_SNIPPETING, true);
4902b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            ((AbstractCursor) cursor).setExtras(extras);
4903b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        }
4904b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        return cursor;
49056ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
49066ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
49076ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    private CrossProcessCursor getCrossProcessCursor(Cursor cursor) {
49086ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        Cursor c = cursor;
49096ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (c instanceof CrossProcessCursor) {
49106ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return (CrossProcessCursor) c;
49116ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else if (c instanceof CursorWindow) {
49126ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return getCrossProcessCursor(((CursorWrapper) c).getWrappedCursor());
49136ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else {
49146ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return null;
49156ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
49166ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
49176ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
49186ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    public MatrixCursor matrixCursorFromCursor(Cursor cursor) {
49196ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        MatrixCursor newCursor = new MatrixCursor(cursor.getColumnNames());
49206ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        int numColumns = cursor.getColumnCount();
49216ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        String data[] = new String[numColumns];
49226ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        cursor.moveToPosition(-1);
49236ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        while (cursor.moveToNext()) {
49246ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            for (int i = 0; i < numColumns; i++) {
49256ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov                data[i] = cursor.getString(i);
49266ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            }
49276ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            newCursor.addRow(data);
4928332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        }
49296ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        return newCursor;
4930d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4931d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4932d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final class DirectoryQuery {
4933d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
4934d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory._ID,
4935d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.DIRECTORY_AUTHORITY,
4936d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_NAME,
4937d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_TYPE
4938d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        };
4939d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4940d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int DIRECTORY_ID = 0;
4941d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int AUTHORITY = 1;
4942d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_NAME = 2;
4943d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_TYPE = 3;
4944d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4945d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4946d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
4947d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Reads and caches directory information for the database.
4948d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
4949d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private DirectoryInfo getDirectoryAuthority(String directoryId) {
49504458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized (mDirectoryCache) {
49514458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            if (!mDirectoryCacheValid) {
49524458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCache.clear();
49535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                SQLiteDatabase db = mDbHelper.get().getReadableDatabase();
495449d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                Cursor cursor = db.query(Tables.DIRECTORIES,
49554458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryQuery.COLUMNS,
49564458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        null, null, null, null, null);
49574458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                try {
49584458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    while (cursor.moveToNext()) {
49594458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryInfo info = new DirectoryInfo();
49604458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        String id = cursor.getString(DirectoryQuery.DIRECTORY_ID);
49614458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.authority = cursor.getString(DirectoryQuery.AUTHORITY);
49624458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
49634458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
49644458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        mDirectoryCache.put(id, info);
49654458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    }
49664458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                } finally {
49674458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    cursor.close();
4968d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                }
49694458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCacheValid = true;
4970d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4971d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
49724458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            return mDirectoryCache.get(directoryId);
49734458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
4974d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4975d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
497672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void resetDirectoryCache() {
49774458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized(mDirectoryCache) {
49784458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            mDirectoryCacheValid = false;
49794458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
498072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
498172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
498223ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki    private boolean hasColumn(String[] projection, String column) {
498323ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki        if (projection == null) {
498423ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki            return true; // Null projection means "all columns".
498523ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki        }
498623ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki
498723ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki        for (int i = 0; i < projection.length; i++) {
498823ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki            if (column.equalsIgnoreCase(projection[i])) return true;
498923ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki        }
499023ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki        return false;
499123ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki    }
499223ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki
49935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    protected Cursor queryLocal(Uri uri, String[] projection, String selection,
49945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            String[] selectionArgs, String sortOrder, long directoryId) {
4995bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
4996bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            Log.v(TAG, "query: " + uri);
4997bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        }
49980b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov
49995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
50005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
5001078f588cef389358adabc579de00747878f3c108Dave Santoro            mActiveDb.set(mContactsHelper.getReadableDatabase());
50025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
500335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
5004d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
50051f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
5006c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limit = getLimit(uri);
5007b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        boolean snippetDeferred = false;
5008c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
50092ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        // The expression used in bundleLetterCountExtras() to get count.
50102ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        String addressBookIndexerCountExpression = null;
50112ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki
5012a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
50134f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
501435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
50155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE:
50165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().query(mActiveDb.get(), projection, selection,
50175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionArgs, sortOrder);
501835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
5019d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
5020763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
50214b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                appendLocalDirectorySelectionIfNeeded(qb, directoryId);
5022619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
5023619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
5024619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
5025d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
50264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
5027763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
50284da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
50294da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
50306bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
50316bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
50326bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
50335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP:
50345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
50355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
50365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int segmentCount = pathSegments.size();
50375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount < 3) {
50385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
5039fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
50405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
5041a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
50425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String lookupKey = pathSegments.get(2);
50435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount == 4) {
50445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
50455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
5046763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
5047a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
50485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
5049a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
5050a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
5051a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
50525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        return c;
50535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
50545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
50555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
5056763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
50574da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
50585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        String.valueOf(lookupContactIdByLookupKey(mActiveDb.get(), lookupKey)));
50594da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
50605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                break;
50615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
50625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
50632149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_DATA:
5064bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_ID_DATA:
5065bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_PHOTO:
5066bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_ID_PHOTO: {
50672149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
50682149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                int segmentCount = pathSegments.size();
50692149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount < 4) {
50705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
50712149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                            "Missing a lookup key", uri));
50722149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
50732149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
50742149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount == 5) {
50752149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
50762149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
50772149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    setTablesAndProjectionMapForData(lookupQb, uri, projection, false);
5078bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    if (match == CONTACTS_LOOKUP_PHOTO || match == CONTACTS_LOOKUP_ID_PHOTO) {
5079bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
5080bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    }
5081a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
50825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
5083a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
5084a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Data.CONTACT_ID, contactId, Data.LOOKUP_KEY, lookupKey);
5085a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
50862149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                        return c;
50872149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    }
50882149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
50892149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    // TODO see if the contact exists but has no data rows (rare)
50902149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
50912149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
50922149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
50935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
50942149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
509524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        String.valueOf(contactId));
5096bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                if (match == CONTACTS_LOOKUP_PHOTO || match == CONTACTS_LOOKUP_ID_PHOTO) {
5097bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
5098bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                }
50992149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                qb.appendWhere(" AND " + Data.CONTACT_ID + "=?");
51002149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                break;
51012149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            }
51022149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
51033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_ID_STREAM_ITEMS: {
51043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                long contactId = Long.parseLong(uri.getPathSegments().get(1));
51053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
51063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
5107af10329f85c5d8c4196c495a9f0f9a6c6ecbc231Daniel Lehmann                qb.appendWhere(StreamItems.CONTACT_ID + "=?");
51083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
51093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
51103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
51113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_LOOKUP_STREAM_ITEMS:
51123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_LOOKUP_ID_STREAM_ITEMS: {
51133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                List<String> pathSegments = uri.getPathSegments();
51143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                int segmentCount = pathSegments.size();
51153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                if (segmentCount < 4) {
51165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
51173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                            "Missing a lookup key", uri));
51183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
51193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String lookupKey = pathSegments.get(2);
51203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                if (segmentCount == 5) {
51213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    long contactId = Long.parseLong(pathSegments.get(3));
51223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
51233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    setTablesAndProjectionMapForStreamItems(lookupQb);
51245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
51253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
5126af10329f85c5d8c4196c495a9f0f9a6c6ecbc231Daniel Lehmann                            StreamItems.CONTACT_ID, contactId,
5127af10329f85c5d8c4196c495a9f0f9a6c6ecbc231Daniel Lehmann                            StreamItems.CONTACT_LOOKUP_KEY, lookupKey);
51283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    if (c != null) {
51293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        return c;
51303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    }
51313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
51323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
51333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
51345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
51353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
51363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(RawContacts.CONTACT_ID + "=?");
51373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
51383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
51393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
5140f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
514142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
51425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
5143ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
5144f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                qb.setProjectionMap(sContactsVCardProjectionMap);
51454da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
514624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        String.valueOf(contactId));
51474da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
5148f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                break;
5149f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            }
5150f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
515142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
515242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
515342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                String currentDateString = dateFormat.format(new Date()).toString();
51545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mActiveDb.get().rawQuery(
515542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    "SELECT" +
515642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " 'vcards_' || ? || '.vcf' AS " + OpenableColumns.DISPLAY_NAME + "," +
515742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " NULL AS " + OpenableColumns.SIZE,
515842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    new String[] { currentDateString });
515942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
516042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
5161ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_FILTER: {
5162916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                String filterParam = "";
5163b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                boolean deferredSnipRequested = deferredSnippetingRequested(uri);
5164ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
5165916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    filterParam = uri.getLastPathSegment();
5166ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
51677ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov                setTablesAndProjectionMapForContactsWithSnippet(
5168b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                        qb, uri, projection, filterParam, directoryId,
5169b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                        deferredSnipRequested);
5170b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                snippetDeferred = isSingleWordQuery(filterParam) &&
5171b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                        deferredSnipRequested && snippetNeeded(projection);
5172ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
5173ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
5174ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
5175ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT_FILTER:
5176ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT: {
51772f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Basically the resultant SQL should look like this:
51782f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // (SQL for listing starred items)
51792f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // UNION ALL
51802f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // (SQL for listing frequently contacted items)
51812f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // ORDER BY ...
51822f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
51832f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final boolean phoneOnly = readBooleanQueryParameter(
51842f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        uri, ContactsContract.STREQUENT_PHONE_ONLY, false);
51852f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (match == CONTACTS_STREQUENT_FILTER && uri.getPathSegments().size() > 3) {
51864a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
51874a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
5188e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
51895e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
51902f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    selection = DbQueryUtils.concatenateClauses(selection, sb.toString());
51914a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
51924a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
51932f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                String[] subProjection = null;
51945e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                if (projection != null) {
51952f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    subProjection = appendProjectionArg(projection, TIMES_USED_SORT_COLUMN);
51965e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                }
51975e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
51984a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                // Build the first query for starred
51994928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                setTablesAndProjectionMapForContacts(qb, uri, projection, false);
52004928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                qb.setProjectionMap(phoneOnly ?
52014928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                        sStrequentPhoneOnlyStarredProjectionMap
52024928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                        : sStrequentStarredProjectionMap);
52039dbfd650ccf93714f3266e80f9fbdbcb526ae7b3Daisuke Miyakawa                if (phoneOnly) {
52045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    qb.appendWhere(DbQueryUtils.concatenateClauses(
52055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            selection, Contacts.HAS_PHONE_NUMBER + "=1"));
52069dbfd650ccf93714f3266e80f9fbdbcb526ae7b3Daisuke Miyakawa                }
52072f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                qb.setStrict(true);
52082f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final String starredQuery = qb.buildQuery(subProjection,
520924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        Contacts.STARRED + "=1", Contacts._ID, null, null, null);
5210d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
52112f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Reset the builder.
5212d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
52132f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                qb.setStrict(true);
52144928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
52154928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                // Build the second query for frequent part.
52164928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                final String frequentQuery;
52174928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                if (phoneOnly) {
52184928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    final StringBuilder tableBuilder = new StringBuilder();
52194928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // In phone only mode, we need to look at view_data instead of
52204928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // contacts/raw_contacts to obtain actual phone numbers. One problem is that
52214928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // view_data is much larger than view_contacts, so our query might become much
52224928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // slower.
52234928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    //
52244928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // To avoid the possible slow down, we start from data usage table and join
52254928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // view_data to the table, assuming data usage table is quite smaller than
52264928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // data rows (almost always it should be), and we don't want any phone
52274928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // numbers not used by the user. This way sqlite is able to drop a number of
52284928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // rows in view_data in the early stage of data lookup.
52294928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    tableBuilder.append(Tables.DATA_USAGE_STAT
52304928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + " INNER JOIN " + Views.DATA + " " + Tables.DATA
52314928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "="
52324928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                                + DataColumns.CONCRETE_ID + " AND "
52334928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "="
52344928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                                + DataUsageStatColumns.USAGE_TYPE_INT_CALL + ")");
52354928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    appendContactPresenceJoin(tableBuilder, projection, RawContacts.CONTACT_ID);
52364928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    appendContactStatusUpdateJoin(tableBuilder, projection,
52374928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            ContactsColumns.LAST_STATUS_UPDATE_ID);
52384928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
52394928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setTables(tableBuilder.toString());
52404928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setProjectionMap(sStrequentPhoneOnlyFrequentProjectionMap);
52414928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.appendWhere(DbQueryUtils.concatenateClauses(
52424928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            selection,
52434928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            Contacts.STARRED + "=0 OR " + Contacts.STARRED + " IS NULL",
52444928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            MimetypesColumns.MIMETYPE + " IN ("
52454928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + "'" + Phone.CONTENT_ITEM_TYPE + "', "
52464928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + "'" + SipAddress.CONTENT_ITEM_TYPE + "')"));
52474928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    frequentQuery = qb.buildQuery(subProjection, null, null, null, null, null);
52484928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                } else {
52494928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    setTablesAndProjectionMapForContacts(qb, uri, projection, true);
52504928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setProjectionMap(sStrequentFrequentProjectionMap);
52514928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.appendWhere(DbQueryUtils.concatenateClauses(
52524928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            selection,
52535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            "(" + Contacts.STARRED + " =0 OR " + Contacts.STARRED + " IS NULL)"));
52544928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    frequentQuery = qb.buildQuery(subProjection,
52554928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            null, Contacts._ID, null, null, null);
52564928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                }
5257d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
5258d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
52592f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final String unionQuery =
52602f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
52612f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                                STREQUENT_ORDER_BY, STREQUENT_LIMIT);
52622f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
52632f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Here, we need to use selection / selectionArgs (supplied from users) "twice",
52642f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // as we want them both for starred items and for frequently contacted items.
52652f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                //
52662f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // e.g. if the user specify selection = "starred =?" and selectionArgs = "0",
52672f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // the resultant SQL should be like:
52682f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // SELECT ... WHERE starred =? AND ...
52692f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // UNION ALL
52702f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // SELECT ... WHERE starred =? AND ...
52712f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                String[] doubledSelectionArgs = null;
52722f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (selectionArgs != null) {
52732f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    final int length = selectionArgs.length;
52742f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    doubledSelectionArgs = new String[length * 2];
52757d7d0e95636344c01eb4e4d034791c199bee98e9Daisuke Miyakawa                    System.arraycopy(selectionArgs, 0, doubledSelectionArgs, 0, length);
52767d7d0e95636344c01eb4e4d034791c199bee98e9Daisuke Miyakawa                    System.arraycopy(selectionArgs, 0, doubledSelectionArgs, length, length);
52772f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                }
52782f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
52795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor cursor = mActiveDb.get().rawQuery(unionQuery, doubledSelectionArgs);
52802f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (cursor != null) {
52812f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    cursor.setNotificationUri(getContext().getContentResolver(),
5282d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
5283d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
52842f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                return cursor;
5285d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
5286d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
528745ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa            case CONTACTS_FREQUENT: {
528845ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                setTablesAndProjectionMapForContacts(qb, uri, projection, true);
528945ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                qb.setProjectionMap(sStrequentFrequentProjectionMap);
529045ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                groupBy = Contacts._ID;
529145ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                if (!TextUtils.isEmpty(sortOrder)) {
529245ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                    sortOrder = FREQUENT_ORDER_BY + ", " + sortOrder;
529345ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                } else {
529445ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                    sortOrder = FREQUENT_ORDER_BY;
529545ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                }
529645ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                break;
529745ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa            }
529845ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa
5299ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_GROUP: {
5300763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
5301b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
530271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
53037cf50494501938f175d288077145acf49da8f171Daniel Lehmann                    String groupMimeTypeId = String.valueOf(
53047cf50494501938f175d288077145acf49da8f171Daniel Lehmann                            mDbHelper.get().getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
53054a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
53067cf50494501938f175d288077145acf49da8f171Daniel Lehmann                    selectionArgs = insertSelectionArg(selectionArgs, groupMimeTypeId);
5307b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
5308b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
5309b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
5310b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
531124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
531224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForContacts(qb, uri, projection);
531324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
531424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
531524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
531624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_ENTITIES: {
531724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForEntities(qb, uri, projection);
531824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
531924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
532024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
532124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD: {
5322ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
532324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.setProjectionMap(sContactsVCardProjectionMap);
532424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
532524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
532624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
5327a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_DATA: {
53284a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
532982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
53304da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
53314da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
53326bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
53336bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
533400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
5335a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
53363653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
533782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
53384da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
53394da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
53403653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
53413653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                break;
53423653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
53433653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
5344a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_ENTITIES: {
5345a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
5346a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
5347a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
5348a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
5349a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
5350a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
5351a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5352a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ENTITIES:
5353a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_ENTITIES: {
5354a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
5355a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                int segmentCount = pathSegments.size();
5356a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount < 4) {
53575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
5358a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            "Missing a lookup key", uri));
5359a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
5360a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
5361a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount == 5) {
5362a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
5363a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
5364a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    setTablesAndProjectionMapForEntities(lookupQb, uri, projection);
5365a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
5366a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
53675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
5368a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
5369a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.CONTACT_ID, contactId,
5370a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.LOOKUP_KEY, lookupKey);
5371a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
5372a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        return c;
5373a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    }
5374a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
5375a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5376a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
5377a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
53785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        String.valueOf(lookupContactIdByLookupKey(mActiveDb.get(), lookupKey)));
5379a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + Contacts.Entity.CONTACT_ID + "=?");
5380a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
5381a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
5382a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
53833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
53843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
53853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
53863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
53873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
53883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
53893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
53903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
53919b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann                qb.appendWhere(StreamItems._ID + "=?");
53923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
53933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
53943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
53953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_LIMIT: {
53966802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                MatrixCursor cursor = new MatrixCursor(new String[]{StreamItems.MAX_ITEMS}, 1);
53976802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                cursor.addRow(new Object[]{MAX_STREAM_ITEMS_PER_RAW_CONTACT});
53983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return cursor;
53993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
54003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
54013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
54023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
54033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
54043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
54053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
54063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
54073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
54083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
54093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemId);
54103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=?");
54113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
54123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
54133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
54143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
54153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
54163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
54173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
54183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemPhotoId);
54193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemId);
54203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=? AND " +
54213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=?");
54223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
54233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
54243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
5425f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case PHOTO_DIMENSIONS: {
5426f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                MatrixCursor cursor = new MatrixCursor(
5427f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{DisplayPhoto.DISPLAY_MAX_DIM, DisplayPhoto.THUMBNAIL_MAX_DIM},
5428f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        1);
5429f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                cursor.addRow(new Object[]{mMaxDisplayPhotoDim, mMaxThumbnailPhotoDim});
5430f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                return cursor;
5431f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
5432f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
54334a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case PHONES: {
543482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
54357cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + "=" +
54367cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        mDbHelper.get().getMimeTypeIdForPhone());
54372ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki
54388ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                final boolean removeDuplicates = readBooleanQueryParameter(
54398ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                        uri, ContactsContract.REMOVE_DUPLICATE_ENTRIES, false);
54408ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                if (removeDuplicates) {
54418ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    groupBy = RawContacts.CONTACT_ID + ", " + Data.DATA1;
54428ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa
54438ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    // In this case, because we dedupe phone numbers, the address book indexer needs
54448ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    // to take it into account too.  (Otherwise headers will appear in wrong
54458ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    // positions.)
54468ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    // So use count(distinct pair(CONTACT_ID, PHONE NUMBER)) instead of count(*).
54478ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    // But because there's no such thing as pair() on sqlite, we use
54488ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    // CONTACT_ID || ',' || PHONE NUMBER instead.
54498ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    // This only slows down the query by 14% with 10,000 contacts.
54508ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    addressBookIndexerCountExpression = "DISTINCT "
54518ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                            + RawContacts.CONTACT_ID + "||','||" + Data.DATA1;
54528ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                }
54532815f58f72f109790585931f601a63ddc02536a5Evan Millar                break;
54542815f58f72f109790585931f601a63ddc02536a5Evan Millar            }
54552815f58f72f109790585931f601a63ddc02536a5Evan Millar
545648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID: {
545782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
54584da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
54597cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
54607cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForPhone());
54614da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
546248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
546348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
546448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
5465ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
546646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
546746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                Integer typeInt = sDataUsageTypeMap.get(typeParam);
546846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (typeInt == null) {
546946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    typeInt = DataUsageStatColumns.USAGE_TYPE_INT_CALL;
547046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
547146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
54727cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
54737cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForPhone());
5474ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
54754a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
54764a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
5477a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    sb.append(" AND (");
54785e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
547945d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    boolean hasCondition = false;
54805e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    boolean orNeeded = false;
5481d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                    final String ftsMatchQuery = SearchIndexManager.getFtsMatchQuery(
5482d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                            filterParam, FtsQueryBuilder.UNSCOPED_NORMALIZING);
5483d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                    if (ftsMatchQuery.length() > 0) {
5484155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN " +
5485155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
5486155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.SEARCH_INDEX +
5487155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " JOIN " + Tables.RAW_CONTACTS +
5488155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
5489155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
5490d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                                " WHERE " + SearchIndexColumns.NAME + " MATCH '");
5491d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                        sb.append(ftsMatchQuery);
5492d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                        sb.append("')");
54935e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        orNeeded = true;
549445d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
54955e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
54965e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
5497892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    String number = PhoneNumberUtils.normalizeNumber(filterParam);
5498892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (!TextUtils.isEmpty(number)) {
54995e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        if (orNeeded) {
55005e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                            sb.append(" OR ");
55015e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        }
55025e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data._ID +
5503892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                " IN (SELECT DISTINCT " + PhoneLookupColumns.DATA_ID
5504892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " FROM " + Tables.PHONE_LOOKUP
5505892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
5506892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append(number);
5507892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append("%')");
550845d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
550945d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    }
551045d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov
551145d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    if (!hasCondition) {
551245d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // If it is neither a phone number nor a name, the query should return
551345d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // an empty cursor.  Let's ensure that.
551445d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        sb.append("0");
55155e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
55165e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
5517a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
5518ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
551958567abca253f1efa2db5c39e17e42dca589e916Daisuke Miyakawa                groupBy = "(CASE WHEN " + PhoneColumns.NORMALIZED_NUMBER
552058567abca253f1efa2db5c39e17e42dca589e916Daisuke Miyakawa                        + " IS NOT NULL THEN " + PhoneColumns.NORMALIZED_NUMBER
552158567abca253f1efa2db5c39e17e42dca589e916Daisuke Miyakawa                        + " ELSE " + Phone.NUMBER + " END), " + RawContacts.CONTACT_ID;
5522a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
552346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
552446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
552546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = accountPromotionSortOrder + ", " + PHONE_FILTER_SORT_ORDER;
552646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    } else {
552746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = PHONE_FILTER_SORT_ORDER;
552846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    }
5529a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
5530ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
5531ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
5532ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
55334a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case EMAILS: {
553482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
55357cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
55367cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForEmail());
55378ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa
55388ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                final boolean removeDuplicates = readBooleanQueryParameter(
55398ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                        uri, ContactsContract.REMOVE_DUPLICATE_ENTRIES, false);
55408ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                if (removeDuplicates) {
55418ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    groupBy = RawContacts.CONTACT_ID + ", " + Data.DATA1;
55428ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa
55438ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    // See PHONES for more detail.
55448ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    addressBookIndexerCountExpression = "DISTINCT "
55458ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                            + RawContacts.CONTACT_ID + "||','||" + Data.DATA1;
55468ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                }
55474a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                break;
55484a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
55494a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
555048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID: {
555182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
55524da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
55537cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
55547cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForEmail()
55554da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        + " AND " + Data._ID + "=?");
555648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
555748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
555848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
55595e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_LOOKUP: {
556082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
55617cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
55627cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForEmail());
55634a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
556408768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String email = uri.getLastPathSegment();
55655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    String address = mDbHelper.get().extractAddressFromEmailAddress(email);
556608768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, address);
556708768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    qb.appendWhere(" AND UPPER(" + Email.DATA + ")=UPPER(?)");
55684a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
5569071d0e7bd3b2f3c9628dd655b09d147e668c3931Daniel Lehmann                // unless told otherwise, we'll return visible before invisible contacts
5570071d0e7bd3b2f3c9628dd655b09d147e668c3931Daniel Lehmann                if (sortOrder == null) {
5571071d0e7bd3b2f3c9628dd655b09d147e668c3931Daniel Lehmann                    sortOrder = "(" + RawContacts.CONTACT_ID + " IN " +
5572071d0e7bd3b2f3c9628dd655b09d147e668c3931Daniel Lehmann                            Tables.DEFAULT_DIRECTORY + ") DESC";
5573071d0e7bd3b2f3c9628dd655b09d147e668c3931Daniel Lehmann                }
5574ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
5575ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
5576ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
55775e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_FILTER: {
557846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
557946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                Integer typeInt = sDataUsageTypeMap.get(typeParam);
558046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (typeInt == null) {
558146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    typeInt = DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT;
558246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
558346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
558407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                String filterParam = null;
55857d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa
558607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
558707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    filterParam = uri.getLastPathSegment();
558807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    if (TextUtils.isEmpty(filterParam)) {
558907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                        filterParam = null;
559007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    }
559107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                }
55925e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
559307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (filterParam == null) {
559407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    // If the filter is unspecified, return nothing
559507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    qb.appendWhere(" AND 0");
559607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                } else {
559707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    StringBuilder sb = new StringBuilder();
559807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(" AND " + Data._ID + " IN (");
559907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(
560007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            "SELECT " + Data._ID +
560107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " FROM " + Tables.DATA +
56022a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                            " WHERE " + DataColumns.MIMETYPE_ID + "=");
56035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    sb.append(mDbHelper.get().getMimeTypeIdForEmail());
56042a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                    sb.append(" AND " + Data.DATA1 + " LIKE ");
560507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(sb, filterParam + '%');
560620938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                    if (!filterParam.contains("@")) {
5607155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(
5608155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " UNION SELECT " + Data._ID +
5609155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.DATA +
5610155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE +" + DataColumns.MIMETYPE_ID + "=");
56115d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        sb.append(mDbHelper.get().getMimeTypeIdForEmail());
5612155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(" AND " + Data.RAW_CONTACT_ID + " IN " +
5613155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
5614155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.SEARCH_INDEX +
5615155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " JOIN " + Tables.RAW_CONTACTS +
5616155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
5617155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
5618d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                                " WHERE " + SearchIndexColumns.NAME + " MATCH '");
5619d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                        final String ftsMatchQuery = SearchIndexManager.getFtsMatchQuery(
5620d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                                filterParam, FtsQueryBuilder.UNSCOPED_NORMALIZING);
5621d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                        sb.append(ftsMatchQuery);
5622d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                        sb.append("')");
56235e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
56245e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
5625a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
56265e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                }
56275e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
5628a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
562946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
563046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
563146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = accountPromotionSortOrder + ", " + EMAIL_FILTER_SORT_ORDER;
56327d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                    } else {
56337d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                        sortOrder = EMAIL_FILTER_SORT_ORDER;
56347d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                    }
5635a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
56365e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                break;
56375e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
56385e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
5639ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
564082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
56417cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
56427cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForStructuredPostal());
56438ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa
56448ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                final boolean removeDuplicates = readBooleanQueryParameter(
56458ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                        uri, ContactsContract.REMOVE_DUPLICATE_ENTRIES, false);
56468ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                if (removeDuplicates) {
56478ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    groupBy = RawContacts.CONTACT_ID + ", " + Data.DATA1;
56488ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa
56498ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    // See PHONES for more detail.
56508ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    addressBookIndexerCountExpression = "DISTINCT "
56518ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                            + RawContacts.CONTACT_ID + "||','||" + Data.DATA1;
56528ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                }
5653ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
5654ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
5655ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
565648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
565782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
56584da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
56597cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
56607cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForStructuredPostal());
56614da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
566248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
566348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
566448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
5665d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS:
5666d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS: {
5667763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
56684f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
56694f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
56704f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
5671d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS_ID:
5672d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID: {
56735ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
5674763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
56754da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
56764da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
56774f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
56784f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
56794f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
5680d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS_DATA:
5681d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID_DATA: {
5682d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                int segment = match == RAW_CONTACTS_DATA ? 1 : 2;
5683d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(segment));
568482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
56854da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
56864da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=?");
568724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
568824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
568924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
56903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case RAW_CONTACTS_ID_STREAM_ITEMS: {
56913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
56923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
56933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
56943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItems.RAW_CONTACT_ID + "=?");
56953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
56963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
569724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
569882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            case RAW_CONTACTS_ID_STREAM_ITEMS_ID: {
569982780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
570082780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                long streamItemId = Long.parseLong(uri.getPathSegments().get(3));
570182780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                setTablesAndProjectionMapForStreamItems(qb);
570282780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(streamItemId));
5703c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
570482780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                qb.appendWhere(StreamItems.RAW_CONTACT_ID + "=? AND " +
570582780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        StreamItems._ID + "=?");
570682780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                break;
570782780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            }
570882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
570924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID_ENTITIES: {
571024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(2));
571124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
571224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForRawEntities(qb, uri);
57135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                qb.appendWhere(" AND " + RawContacts._ID + "=?");
5714e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
5715e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
5716e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
5717d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case DATA:
5718d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_DATA: {
571982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
5720e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
5721e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
5722e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
5723d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case DATA_ID:
5724d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_DATA_ID: {
572582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
57264da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
57274da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
5728a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                break;
5729a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            }
5730a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov
573185077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro            case PROFILE_PHOTO: {
573285077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
573385077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
573485077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                break;
573585077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro            }
573685077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro
5737a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
5738e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                // Phone lookup cannot be combined with a selection
5739e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selection = null;
5740e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selectionArgs = null;
574158795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                if (uri.getBooleanQueryParameter(PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS, false)) {
574258795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    if (TextUtils.isEmpty(sortOrder)) {
574358795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                        // Default the sort order to something reasonable so we get consistent
574458795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                        // results when callers don't request an ordering
574558795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                        sortOrder = Contacts.DISPLAY_NAME + " ASC";
574658795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    }
574758795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda
574858795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    String sipAddress = uri.getPathSegments().size() > 1
574958795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                            ? Uri.decode(uri.getLastPathSegment()) : "";
575058795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    setTablesAndProjectionMapForData(qb, uri, null, false, true);
575158795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    StringBuilder sb = new StringBuilder();
575258795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    selectionArgs = mDbHelper.get().buildSipContactQuery(sb, sipAddress);
575358795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    selection = sb.toString();
575458795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                } else {
575558795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    if (TextUtils.isEmpty(sortOrder)) {
575658795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                        // Default the sort order to something reasonable so we get consistent
575758795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                        // results when callers don't request an ordering
575858795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                        sortOrder = " length(lookup.normalized_number) DESC";
575958795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    }
576058795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda
576158795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    String number = uri.getPathSegments().size() > 1
576258795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                            ? uri.getLastPathSegment() : "";
576358795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    String numberE164 = PhoneNumberUtils.formatNumberToE164(number,
576458795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                            mDbHelper.get().getCurrentCountryIso());
576558795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    String normalizedNumber =
576658795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                            PhoneNumberUtils.normalizeNumber(number);
576758795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    mDbHelper.get().buildPhoneLookupAndContactQuery(
576858795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                            qb, normalizedNumber, numberE164);
576958795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    qb.setProjectionMap(sPhoneLookupProjectionMap);
577058795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                }
5771a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
5772a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
5773a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
5774ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
5775ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.GROUPS);
5776ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
5777f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                appendAccountFromParameter(qb, uri);
5778ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5779ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5780ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5781ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
5782ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.GROUPS);
5783ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
57844da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
57854da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Groups._ID + "=?");
5786ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5787ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5788ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5789ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
5790f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                final boolean returnGroupCountPerAccount =
5791f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        readBooleanQueryParameter(uri, Groups.PARAM_RETURN_GROUP_COUNT_PER_ACCOUNT,
5792f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                                false);
579323ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki                String tables = Views.GROUPS + " AS " + Tables.GROUPS;
579423ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki                if (hasColumn(projection, Groups.SUMMARY_COUNT)) {
579523ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki                    tables = tables + Joins.GROUP_MEMBER_COUNT;
579623ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki                }
579723ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki                qb.setTables(tables);
5798f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                qb.setProjectionMap(returnGroupCountPerAccount ?
5799f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        sGroupsSummaryProjectionMapWithGroupCountPerAccount
5800f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        : sGroupsSummaryProjectionMap);
5801f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                appendAccountFromParameter(qb, uri);
5802f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                groupBy = GroupsColumns.CONCRETE_ID;
5803ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5804ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5805ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5806b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
58070c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
5808b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
5809b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
5810b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
5811b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
581231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
5813d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
58142d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                String filter = null;
58152d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
58162d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                    filter = uri.getPathSegments().get(3);
58172d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                }
581831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
5819d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                if (limit != null) {
5820d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(limit);
582131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
582231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
582331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
582431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
58255b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                ArrayList<AggregationSuggestionParameter> parameters = null;
58265b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                List<String> query = uri.getQueryParameters("query");
58275b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                if (query != null && !query.isEmpty()) {
58285b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    parameters = new ArrayList<AggregationSuggestionParameter>(query.size());
58295b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    for (String parameter : query) {
58305b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        int offset = parameter.indexOf(':');
58315b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        parameters.add(offset == -1
58325b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                ? new AggregationSuggestionParameter(
583376dfa406e2cde19c824983c37fc92c1c5bf63eecDaniel Lehmann                                        AggregationSuggestions.PARAMETER_MATCH_NAME,
58345b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter)
58355b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                : new AggregationSuggestionParameter(
58365b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(0, offset),
58375b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(offset + 1)));
58385b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    }
58395b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                }
58405b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
5841763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
58427581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov
58435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mAggregator.get().queryAggregationSuggestions(qb, projection, contactId,
58445b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        maxSuggestions, filter, parameters);
584531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
584631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
5847eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
5848eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setTables(Tables.SETTINGS);
5849eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setProjectionMap(sSettingsProjectionMap);
5850f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                appendAccountFromParameter(qb, uri);
5851e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
5852e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // When requesting specific columns, this query requires
5853e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // late-binding of the GroupMembership MIME-type.
58545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                final String groupMembershipMimetypeId = Long.toString(mDbHelper.get()
5855e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
585682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
58575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mDbHelper.get().isInProjection(projection, Settings.UNGROUPED_COUNT)) {
5858e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
5859e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
586082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
58615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mDbHelper.get().isInProjection(
58625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                                projection, Settings.UNGROUPED_WITH_PHONES)) {
5863e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
5864e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
5865e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
5866eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
5867eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
5868eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
58695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case STATUS_UPDATES:
58705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_STATUS_UPDATES: {
58710a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
58725ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
58735ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
58745ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
587582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES_ID: {
58760a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
58774da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
58784da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(DataColumns.CONCRETE_ID + "=?");
58795ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
58805ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
58815ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
5882c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS: {
5883174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(
58845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mActiveDb.get(), uri, projection, limit);
5885c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5886c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
5887c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT: {
58882d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                String lookupKey = uri.getLastPathSegment();
5889174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                String filter = getQueryParameter(
5890174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        uri, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
5891174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchShortcutRefresh(
58925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mActiveDb.get(), projection, lookupKey, filter);
5893c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5894c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
58953202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro            case RAW_CONTACT_ENTITIES:
58963202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro            case PROFILE_RAW_CONTACT_ENTITIES: {
5897a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
589846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
589946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
590046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
590146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITY_ID: {
590246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
5903a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
59044da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
59054da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
590646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
590746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
590846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
590909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            case PROVIDER_STATUS: {
591009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                return queryProviderStatus(uri, projection);
591109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
591209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5913d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES : {
5914d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
5915d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
5916d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
5917d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
5918d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
5919d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID : {
5920385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                long id = ContentUris.parseId(uri);
5921d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
5922d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
5923385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(id));
5924d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.appendWhere(Directory._ID + "=?");
5925d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
5926d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
5927d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
59287a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            case COMPLETE_NAME: {
59297a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                return completeName(uri, projection);
59307a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
59317a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
59324f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
5933f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
5934c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                        sortOrder, limit);
59354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
59364f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
593709e69522745551522c55dff27424496f255def46Daniel Lehmann        qb.setStrict(true);
59387f786e5cbde9975b9632beb9b6d19eeef8a64cf1Dmitri Plotnikov
5939ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        Cursor cursor =
59405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                query(mActiveDb.get(), qb, projection, selection, selectionArgs, sortOrder, groupBy,
59415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        limit);
5942ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (readBooleanQueryParameter(uri, ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, false)) {
59435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            cursor = bundleLetterCountExtras(cursor, mActiveDb.get(), qb, selection,
59442ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                    selectionArgs, sortOrder, addressBookIndexerCountExpression);
5945ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5946b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        if (snippetDeferred) {
5947b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            cursor = addDeferredSnippetingExtra(cursor);
5948b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        }
5949ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        return cursor;
59505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
59515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
59525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
59535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String selection, String[] selectionArgs, String sortOrder, String groupBy,
59545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String limit) {
5955038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        if (projection != null && projection.length == 1
5956038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                && BaseColumns._COUNT.equals(projection[0])) {
5957038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            qb.setProjectionMap(sCountProjectionMap);
5958038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
59595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
59605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sortOrder, limit);
59614f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
59624f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
59634f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
59644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
59654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
59664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
596709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    /**
596809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     * Creates a single-row cursor containing the current status of the provider.
596909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     */
597009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private Cursor queryProviderStatus(Uri uri, String[] projection) {
597109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
597209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        RowBuilder row = cursor.newRow();
597309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
597409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            if (ProviderStatus.STATUS.equals(projection[i])) {
597509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mProviderStatus);
597609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            } else if (ProviderStatus.DATA1.equals(projection[i])) {
597709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mEstimatedStorageRequirement);
597809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
597909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        }
598009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        return cursor;
598109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    }
598209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5983a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /**
5984a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * Runs the query with the supplied contact ID and lookup ID.  If the query succeeds,
5985a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * it returns the resulting cursor, otherwise it returns null and the calling
5986a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * method needs to resolve the lookup key and rerun the query.
5987a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     */
5988a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private Cursor queryWithContactIdAndLookupKey(SQLiteQueryBuilder lookupQb,
5989a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            SQLiteDatabase db, Uri uri,
5990a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection, String selection, String[] selectionArgs,
5991a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String sortOrder, String groupBy, String limit,
5992a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn, long contactId, String lookupKeyColumn, String lookupKey) {
5993a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        String[] args;
5994a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (selectionArgs == null) {
5995a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[2];
5996a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        } else {
5997a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[selectionArgs.length + 2];
5998a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
5999a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
6000a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[0] = String.valueOf(contactId);
6001a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[1] = Uri.encode(lookupKey);
6002a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        lookupQb.appendWhere(contactIdColumn + "=? AND " + lookupKeyColumn + "=?");
6003a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        Cursor c = query(db, lookupQb, projection, selection, args, sortOrder,
6004a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                groupBy, limit);
6005a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (c.getCount() != 0) {
6006a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return c;
6007a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
6008a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6009a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        c.close();
6010a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return null;
6011a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
601209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
6013bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov    private static final class AddressBookIndexQuery {
6014bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String LETTER = "letter";
6015bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String TITLE = "title";
6016bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String COUNT = "count";
6017ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6018bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
6019bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                LETTER, TITLE, COUNT
6020ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        };
6021ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6022bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_LETTER = 0;
6023bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_TITLE = 1;
6024bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_COUNT = 2;
6025bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
60265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // The first letter of the sort key column is what is used for the index headings.
60275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        public static final String SECTION_HEADING = "SUBSTR(%1$s,1,1)";
602824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
6029de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa        public static final String ORDER_BY = LETTER + " COLLATE " + PHONEBOOK_COLLATOR_NAME;
6030ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
6031ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6032ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    /**
6033ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * Computes counts by the address book index titles and adds the resulting tally
6034ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * to the returned cursor as a bundle of extras.
6035ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     */
6036ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    private Cursor bundleLetterCountExtras(Cursor cursor, final SQLiteDatabase db,
60372ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki            SQLiteQueryBuilder qb, String selection, String[] selectionArgs, String sortOrder,
60382ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki            String countExpression) {
6039409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki        if (!(cursor instanceof AbstractCursor)) {
6040409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            Log.w(TAG, "Unable to bundle extras.  Cursor is not AbstractCursor.");
6041409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            return cursor;
6042409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki        }
6043ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortKey;
6044ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6045ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // The sort order suffix could be something like "DESC".
6046ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // We want to preserve it in the query even though we will change
6047ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // the sort column itself.
6048ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortOrderSuffix = "";
6049ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (sortOrder != null) {
6050ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int spaceIndex = sortOrder.indexOf(' ');
6051ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            if (spaceIndex != -1) {
6052ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder.substring(0, spaceIndex);
6053ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortOrderSuffix = sortOrder.substring(spaceIndex);
6054ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            } else {
6055ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder;
6056ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
6057ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } else {
6058ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            sortKey = Contacts.SORT_KEY_PRIMARY;
6059ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
6060ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6061bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        String locale = getLocale().toString();
6062ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        HashMap<String, String> projectionMap = Maps.newHashMap();
60635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        String sectionHeading = String.format(AddressBookIndexQuery.SECTION_HEADING, sortKey);
6064bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.LETTER,
606524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                sectionHeading + " AS " + AddressBookIndexQuery.LETTER);
6066bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
60672ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        // If "what to count" is not specified, we just count all records.
60682ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        if (TextUtils.isEmpty(countExpression)) {
60692ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki            countExpression = "*";
60702ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        }
60712ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki
6072bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        /**
6073bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * Use the GET_PHONEBOOK_INDEX function, which is an android extension for SQLite3,
6074bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * to map the first letter of the sort key to a character that is traditionally
6075bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * used in phonebooks to represent that letter.  For example, in Korean it will
6076bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * be the first consonant in the letter; for Japanese it will be Hiragana rather
6077bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * than Katakana.
6078bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         */
6079ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.TITLE,
608024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                "GET_PHONEBOOK_INDEX(" + sectionHeading + ",'" + locale + "')"
6081bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                        + " AS " + AddressBookIndexQuery.TITLE);
6082ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.COUNT,
60832ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                "COUNT(" + countExpression + ") AS " + AddressBookIndexQuery.COUNT);
6084ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        qb.setProjectionMap(projectionMap);
6085ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6086f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov        Cursor indexCursor = qb.query(db, AddressBookIndexQuery.COLUMNS, selection, selectionArgs,
6087ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY, null /* having */,
6088ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY + sortOrderSuffix);
6089ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6090ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        try {
6091f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            int groupCount = indexCursor.getCount();
6092ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            String titles[] = new String[groupCount];
6093ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int counts[] = new int[groupCount];
6094bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            int indexCount = 0;
6095bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            String currentTitle = null;
6096bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
6097bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // Since GET_PHONEBOOK_INDEX is a many-to-1 function, we may end up
6098bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // with multiple entries for the same title.  The following code
6099bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // collapses those duplicates.
6100ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            for (int i = 0; i < groupCount; i++) {
6101f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov                indexCursor.moveToNext();
6102bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String title = indexCursor.getString(AddressBookIndexQuery.COLUMN_TITLE);
6103bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int count = indexCursor.getInt(AddressBookIndexQuery.COLUMN_COUNT);
6104bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                if (indexCount == 0 || !TextUtils.equals(title, currentTitle)) {
6105bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    titles[indexCount] = currentTitle = title;
6106bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount] = count;
6107bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    indexCount++;
6108bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                } else {
6109bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount - 1] += count;
6110bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                }
6111bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            }
6112bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
6113bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            if (indexCount < groupCount) {
6114bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String[] newTitles = new String[indexCount];
6115bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(titles, 0, newTitles, 0, indexCount);
6116bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                titles = newTitles;
6117bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
6118bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int[] newCounts = new int[indexCount];
6119bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(counts, 0, newCounts, 0, indexCount);
6120bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                counts = newCounts;
6121ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
6122ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6123409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            final Bundle bundle = new Bundle();
6124409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            bundle.putStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES, titles);
6125409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            bundle.putIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS, counts);
6126409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki
6127409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            ((AbstractCursor) cursor).setExtras(bundle);
6128409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            return cursor;
6129ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } finally {
6130f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            indexCursor.close();
6131ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
6132ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
6133ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
61342d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    /**
613592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Returns the contact Id for the contact identified by the lookupKey.
613692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Robust against changes in the lookup key: if the key has changed, will
613792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * look up the contact by the raw contact IDs or name encoded in the lookup
613892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * key.
61392d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     */
61402d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    public long lookupContactIdByLookupKey(SQLiteDatabase db, String lookupKey) {
61415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactLookupKey key = new ContactLookupKey();
61425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ArrayList<LookupKeySegment> segments = key.parse(lookupKey);
61435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
614492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        long contactId = -1;
61455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_PROFILE)) {
61465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            // We should already be in a profile database context, so just look up a single contact.
61475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro           contactId = lookupSingleContactId(db);
61485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
61495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
615092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_SOURCE_ID)) {
615192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdBySourceIds(db, segments);
615292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
615392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
615492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
615592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
615692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
615792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        boolean hasRawContactIds =
615892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID);
615992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds) {
616092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdByRawContactIds(db, segments);
616192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
616292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
616392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
616492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
616592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
616692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds
616792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                || lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME)) {
61685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            contactId = lookupContactIdByDisplayNames(db, segments);
61695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
61705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
61715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return contactId;
61725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
61735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
61745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private long lookupSingleContactId(SQLiteDatabase db) {
61755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = db.query(Tables.CONTACTS, new String[] {Contacts._ID},
61765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                null, null, null, null, null, "1");
61775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        try {
61785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (c.moveToFirst()) {
61795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return c.getLong(0);
61805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            } else {
61815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return -1;
61825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
61835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } finally {
61845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            c.close();
61855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
61865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
61875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
61885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupBySourceIdQuery {
618943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String TABLE = Views.RAW_CONTACTS;
61905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
61915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
61925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
619343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
61945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
61955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.SOURCE_ID
61965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
61975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
61985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
619943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int ACCOUNT_TYPE_AND_DATA_SET = 1;
62005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
62015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int SOURCE_ID = 3;
62025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
62035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdBySourceIds(SQLiteDatabase db,
62055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
62065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
62075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(RawContacts.SOURCE_ID + " IN (");
62085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
62095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
621092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID) {
62115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
62125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
62135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
62145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
62155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
62165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
62175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupBySourceIdQuery.TABLE, LookupBySourceIdQuery.COLUMNS,
62195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
62205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
62215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
622243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                String accountTypeAndDataSet =
622343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE_AND_DATA_SET);
62245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME);
62255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
622643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        ContactLookupKey.getAccountHashCode(accountTypeAndDataSet, accountName);
62275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID);
62285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
62295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
623092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID
623192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
62325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(sourceId)) {
62335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupBySourceIdQuery.CONTACT_ID);
62345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
62355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
62365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
62375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
62385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
62395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
62405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
62415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
62435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
62445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
624592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByRawContactIdQuery {
624643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String TABLE = Views.RAW_CONTACTS;
62475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
62495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
625043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
62515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
625292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts._ID,
62535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
62545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
625643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int ACCOUNT_TYPE_AND_DATA_SET = 1;
62575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
625892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ID = 3;
62595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
62605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
626192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByRawContactIds(SQLiteDatabase db,
626292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            ArrayList<LookupKeySegment> segments) {
626392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
626492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(RawContacts._ID + " IN (");
62655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
62665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
626792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
626892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(segment.rawContactId);
626992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(",");
62705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
62715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
627292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
627392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
62745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
627592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        Cursor c = db.query(LookupByRawContactIdQuery.TABLE, LookupByRawContactIdQuery.COLUMNS,
627692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                 sb.toString(), null, null, null, null);
627792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        try {
627892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            while (c.moveToNext()) {
627943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                String accountTypeAndDataSet = c.getString(
628043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        LookupByRawContactIdQuery.ACCOUNT_TYPE_AND_DATA_SET);
628192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountName = c.getString(LookupByRawContactIdQuery.ACCOUNT_NAME);
628292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                int accountHashCode =
628343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        ContactLookupKey.getAccountHashCode(accountTypeAndDataSet, accountName);
628492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String rawContactId = c.getString(LookupByRawContactIdQuery.ID);
628592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
628692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
628792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID
628892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
628992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && segment.rawContactId.equals(rawContactId)) {
629092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        segment.contactId = c.getLong(LookupByRawContactIdQuery.CONTACT_ID);
629192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        break;
629292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    }
629392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                }
629492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
629592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        } finally {
629692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            c.close();
62975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
62985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
629992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return getMostReferencedContactId(segments);
630092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
630192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
630292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByDisplayNameQuery {
630392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
630492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
630592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String COLUMNS[] = {
630692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.CONTACT_ID,
630743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
630892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
630992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME
631092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        };
631192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
631292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int CONTACT_ID = 0;
631343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int ACCOUNT_TYPE_AND_DATA_SET = 1;
631492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_NAME = 2;
631592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int NORMALIZED_NAME = 3;
631692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
631792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
631892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByDisplayNames(SQLiteDatabase db,
631992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
63205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
63215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(NameLookupColumns.NORMALIZED_NAME + " IN (");
63225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
63235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
632492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
632592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
63265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
63275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
63285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
63295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
63305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
63315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
63325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                + " AND " + RawContacts.CONTACT_ID + " NOT NULL");
63335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
63345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupByDisplayNameQuery.TABLE, LookupByDisplayNameQuery.COLUMNS,
63355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
63365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
63375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
633843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                String accountTypeAndDataSet =
633943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE_AND_DATA_SET);
63405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME);
63415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
634243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        ContactLookupKey.getAccountHashCode(accountTypeAndDataSet, accountName);
63435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME);
63445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
63455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
634692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if ((segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
634792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID)
634892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
63495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(name)) {
63505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupByDisplayNameQuery.CONTACT_ID);
63515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
63525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
63535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
63545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
63555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
63565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
63575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
63585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
63595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
63605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
63615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
636292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private boolean lookupKeyContainsType(ArrayList<LookupKeySegment> segments, int lookupType) {
636392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
636492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            LookupKeySegment segment = segments.get(i);
636592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == lookupType) {
636692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return true;
636792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
636892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
636992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
637092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return false;
637192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
637292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
6373ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateLookupKeyForRawContact(SQLiteDatabase db, long rawContactId) {
63745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().updateLookupKeyForRawContact(db, rawContactId);
6375ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    }
6376ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov
63775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /**
63785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     * Returns the contact ID that is mentioned the highest number of times.
63795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     */
63805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long getMostReferencedContactId(ArrayList<LookupKeySegment> segments) {
63815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Collections.sort(segments);
63825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
63835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long bestContactId = -1;
63845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int bestRefCount = 0;
63855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
63865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = -1;
63875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int count = 0;
63885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
63895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int segmentCount = segments.size();
63905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segmentCount; i++) {
63915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
63925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.contactId != -1) {
63935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segment.contactId == contactId) {
63945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count++;
63955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                } else {
63965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (count > bestRefCount) {
63975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestContactId = contactId;
63985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestRefCount = count;
63995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
64005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    contactId = segment.contactId;
64015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count = 1;
64025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
64035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
64045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
64055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count > bestRefCount) {
64065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return contactId;
64075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
64085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return bestContactId;
64095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
64105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
64115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
6412763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
6413763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            String[] projection) {
64144928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa        setTablesAndProjectionMapForContacts(qb, uri, projection, false);
64152f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    }
64162f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
64172f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    /**
64184928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * @param includeDataUsageStat true when the table should include DataUsageStat table.
64194928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * Note that this uses INNER JOIN instead of LEFT OUTER JOIN, so some of data in Contacts
64204928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * may be dropped.
64212f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     */
64222f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
64234928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            String[] projection, boolean includeDataUsageStat) {
642482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
6425ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.CONTACTS);
64262f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
64272f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa        // Just for frequently contacted contacts in Strequent Uri handling.
64284928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa        if (includeDataUsageStat) {
64292f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            sb.append(" INNER JOIN " +
6430ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                    Views.DATA_USAGE_STAT + " AS " + Tables.DATA_USAGE_STAT +
64312f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    " ON (" +
64322f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    DbQueryUtils.concatenateClauses(
64332f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                            DataUsageStatColumns.CONCRETE_TIMES_USED + " > 0",
64344928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            RawContacts.CONTACT_ID + "=" + Views.CONTACTS + "." + Contacts._ID) +
64352f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    ")");
64362f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa        }
64372f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
64387ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
64397ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
6440916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setTables(sb.toString());
6441916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionMap);
6442916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    }
6443916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
6444916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /**
6445916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * Finds name lookup records matching the supplied filter, picks one arbitrary match per
6446916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * contact and joins that with other contacts tables.
6447916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     */
6448916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private void setTablesAndProjectionMapForContactsWithSnippet(SQLiteQueryBuilder qb, Uri uri,
6449b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            String[] projection, String filter, long directoryId, boolean deferredSnippeting) {
64507ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov
64517ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
6452ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.CONTACTS);
6453916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
645403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        if (filter != null) {
645503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            filter = filter.trim();
645603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        }
645703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
645830cc766756461da8d53933f88ea01dd2272a90ebDmitri Plotnikov        if (TextUtils.isEmpty(filter) || (directoryId != -1 && directoryId != Directory.DEFAULT)) {
645930cc766756461da8d53933f88ea01dd2272a90ebDmitri Plotnikov            sb.append(" JOIN (SELECT NULL AS " + SearchSnippetColumns.SNIPPET + " WHERE 0)");
64605e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        } else {
6461b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            appendSearchIndexJoin(sb, uri, projection, filter, deferredSnippeting);
64625e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        }
64637ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
64647ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
646503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        qb.setTables(sb.toString());
646603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionWithSnippetMap);
646703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    }
6468916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
646903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private void appendSearchIndexJoin(
6470b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            StringBuilder sb, Uri uri, String[] projection, String filter,
6471b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            boolean  deferredSnippeting) {
6472916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
6473b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        if (snippetNeeded(projection)) {
647403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String[] args = null;
647503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String snippetArgs =
647603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                    getQueryParameter(uri, SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY);
647703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            if (snippetArgs != null) {
647803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                args = snippetArgs.split(",");
647903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
648003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
64815e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String startMatch = args != null && args.length > 0 ? args[0]
64825e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_START_MATCH;
64835e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String endMatch = args != null && args.length > 1 ? args[1]
64845e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_END_MATCH;
64855e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String ellipsis = args != null && args.length > 2 ? args[2]
64865e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_ELLIPSIS;
64875e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            int maxTokens = args != null && args.length > 3 ? Integer.parseInt(args[3])
64885e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_MAX_TOKENS;
64895e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
6490174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            appendSearchIndexJoin(
6491b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                    sb, filter, true, startMatch, endMatch, ellipsis, maxTokens,
6492b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                    deferredSnippeting);
6493174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        } else {
6494b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            appendSearchIndexJoin(sb, filter, false, null, null, null, 0, false);
6495174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
6496174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    }
6497174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
6498174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    public void appendSearchIndexJoin(StringBuilder sb, String filter,
6499174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            boolean snippetNeeded, String startMatch, String endMatch, String ellipsis,
6500b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            int maxTokens, boolean deferredSnippeting) {
6501174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        boolean isEmailAddress = false;
6502174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String emailAddress = null;
6503174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        boolean isPhoneNumber = false;
6504174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String phoneNumber = null;
6505174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String numberE164 = null;
6506174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
65073716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // If the query consists of a single word, we can do snippetizing after-the-fact for a
65083716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // performance boost.
6509b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        boolean singleTokenSearch = isSingleWordQuery(filter);
65103716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
6511174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (filter.indexOf('@') != -1) {
65125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            emailAddress = mDbHelper.get().extractAddressFromEmailAddress(filter);
6513174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            isEmailAddress = !TextUtils.isEmpty(emailAddress);
6514174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        } else {
6515174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            isPhoneNumber = isPhoneNumber(filter);
651604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            if (isPhoneNumber) {
651704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                phoneNumber = PhoneNumberUtils.normalizeNumber(filter);
651804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                numberE164 = PhoneNumberUtils.formatNumberToE164(phoneNumber,
65195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mDbHelper.get().getCountryIso());
652004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            }
6521174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
6522174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
6523d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann        final String SNIPPET_CONTACT_ID = "snippet_contact_id";
6524d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann        sb.append(" JOIN (SELECT " + SearchIndexColumns.CONTACT_ID + " AS " + SNIPPET_CONTACT_ID);
6525174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (snippetNeeded) {
65265e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            sb.append(", ");
65275e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            if (isEmailAddress) {
65283d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append("ifnull(");
65295e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, startMatch);
653004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("||(SELECT MIN(" + Email.ADDRESS + ")");
653104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" FROM " + Tables.DATA_JOIN_RAW_CONTACTS);
653204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" WHERE  " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
653304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + RawContacts.CONTACT_ID + " AND " + Email.ADDRESS + " LIKE ");
653404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                DatabaseUtils.appendEscapedSQLString(sb, filter + "%");
653504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(")||");
65363d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, endMatch);
65373d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append(",");
65383716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
6539b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                // Optimization for single-token search (do only if requested).
6540b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                if (singleTokenSearch && deferredSnippeting) {
65413716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    sb.append(SearchIndexColumns.CONTENT);
65423716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                } else {
65433716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
65443716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                }
65453d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append(")");
65463d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov            } else if (isPhoneNumber) {
65473d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append("ifnull(");
65483d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, startMatch);
654904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("||(SELECT MIN(" + Phone.NUMBER + ")");
655004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" FROM " +
655104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                        Tables.DATA_JOIN_RAW_CONTACTS + " JOIN " + Tables.PHONE_LOOKUP);
655204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" ON " + DataColumns.CONCRETE_ID);
655304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + Tables.PHONE_LOOKUP + "." + PhoneLookupColumns.DATA_ID);
655404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" WHERE  " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
655504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + RawContacts.CONTACT_ID);
655604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" AND " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
655704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(phoneNumber);
655804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("%'");
655904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                if (!TextUtils.isEmpty(numberE164)) {
656004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append(" OR " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
656104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append(numberE164);
656204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append("%'");
656304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                }
656404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(")||");
65655e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, endMatch);
65665e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                sb.append(",");
65673716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
6568b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                // Optimization for single-token search (do only if requested).
6569b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                if (singleTokenSearch && deferredSnippeting) {
65703716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    sb.append(SearchIndexColumns.CONTENT);
65713716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                } else {
65723716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
65733716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                }
65745e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                sb.append(")");
657503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            } else {
657604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                final String normalizedFilter = NameNormalizer.normalize(filter);
657704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                if (!TextUtils.isEmpty(normalizedFilter)) {
6578b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                    // Optimization for single-token search (do only if requested)..
6579b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                    if (singleTokenSearch && deferredSnippeting) {
65803716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(SearchIndexColumns.CONTENT);
65813716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    } else {
65823716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("(CASE WHEN EXISTS (SELECT 1 FROM ");
65833716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.RAW_CONTACTS + " AS rc INNER JOIN ");
65843716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.NAME_LOOKUP + " AS nl ON (rc." + RawContacts._ID);
65853716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("=nl." + NameLookupColumns.RAW_CONTACT_ID);
65863716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(") WHERE nl." + NameLookupColumns.NORMALIZED_NAME);
65873716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(" GLOB '" + normalizedFilter + "*' AND ");
65883716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("nl." + NameLookupColumns.NAME_TYPE + "=");
65893716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(NameLookupType.NAME_COLLATION_KEY + " AND ");
65903716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
65913716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("=rc." + RawContacts.CONTACT_ID);
65923716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(") THEN NULL ELSE ");
65933716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
65943716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(" END)");
65953716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    }
659604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                } else {
659704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append("NULL");
659804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                }
659903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
66005e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            sb.append(" AS " + SearchSnippetColumns.SNIPPET);
66015e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        }
660203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
66035e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(" FROM " + Tables.SEARCH_INDEX);
66045e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(" WHERE ");
6605d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann        sb.append(Tables.SEARCH_INDEX + " MATCH '");
66065e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        if (isEmailAddress) {
6607d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            // we know that the emailAddress contains a @. This phrase search should be
6608d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            // scoped against "content:" only, but unfortunately SQLite doesn't support
6609d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            // phrases and scoped columns at once. This is fine in this case however, because:
6610d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            //  - We can't erronously match against name, as name is all-hex (so the @ can't match)
6611d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            //  - We can't match against tokens, because phone-numbers can't contain @
6612d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            final String sanitizedEmailAddress =
6613d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                    emailAddress == null ? "" : sanitizeMatch(emailAddress);
6614d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            sb.append("\"");
6615d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            sb.append(sanitizedEmailAddress);
6616d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            sb.append("*\"");
66173d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov        } else if (isPhoneNumber) {
6618d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            // normalized version of the phone number (phoneNumber can only have + and digits)
6619d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            final String phoneNumberCriteria = " OR tokens:" + phoneNumber + "*";
6620d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann
6621d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            // international version of this number (numberE164 can only have + and digits)
6622d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            final String numberE164Criteria =
6623d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                    (numberE164 != null && !TextUtils.equals(numberE164, phoneNumber))
6624d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                    ? " OR tokens:" + numberE164 + "*"
6625d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                    : "";
6626d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann
6627d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            // combine all criteria
6628d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            final String commonCriteria =
6629d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                    phoneNumberCriteria + numberE164Criteria;
6630d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann
6631d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            // search in content
6632d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            sb.append(SearchIndexManager.getFtsMatchQuery(filter,
6633d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                    FtsQueryBuilder.getDigitsQueryBuilder(commonCriteria)));
663403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        } else {
6635d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            // general case: not a phone number, not an email-address
6636d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            sb.append(SearchIndexManager.getFtsMatchQuery(filter,
6637d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                    FtsQueryBuilder.SCOPED_NAME_NORMALIZING));
66389c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        }
66391322df8f90d80587748ad10539516635326c01e8Daniel Lehmann        // Omit results in "Other Contacts".
66401322df8f90d80587748ad10539516635326c01e8Daniel Lehmann        sb.append("' AND " + SNIPPET_CONTACT_ID + " IN " + Tables.DEFAULT_DIRECTORY + ")");
66411322df8f90d80587748ad10539516635326c01e8Daniel Lehmann        sb.append(" ON (" + Contacts._ID + "=" + SNIPPET_CONTACT_ID + ")");
6642a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov    }
6643a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov
6644d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann    private static String sanitizeMatch(String filter) {
6645d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann        return filter.replace("'", "").replace("*", "").replace("-", "").replace("\"", "");
66462352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov    }
66472352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov
66485e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov    private void appendSnippetFunction(
66495e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            StringBuilder sb, String startMatch, String endMatch, String ellipsis, int maxTokens) {
66505e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append("snippet(" + Tables.SEARCH_INDEX + ",");
66515e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, startMatch);
66525e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",");
66535e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, endMatch);
66545e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",");
66555e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, ellipsis);
66565e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
66575e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        // The index of the column used for the snippet, "content"
66585e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",1,");
66595e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(maxTokens);
66605e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(")");
66615e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov    }
66625e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
6663763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForRawContacts(SQLiteQueryBuilder qb, Uri uri) {
6664763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        StringBuilder sb = new StringBuilder();
6665ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.RAW_CONTACTS);
6666763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setTables(sb.toString());
6667763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setProjectionMap(sRawContactsProjectionMap);
6668f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        appendAccountFromParameter(qb, uri);
6669763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    }
6670763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
6671a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForRawEntities(SQLiteQueryBuilder qb, Uri uri) {
6672ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        qb.setTables(Views.RAW_ENTITIES);
6673a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sRawEntityProjectionMap);
6674f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        appendAccountFromParameter(qb, uri);
667546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    }
667646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
667782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
667882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String[] projection, boolean distinct) {
667958795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda        setTablesAndProjectionMapForData(qb, uri, projection, distinct, false, null);
668058795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    }
668158795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda
668258795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
668358795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            String[] projection, boolean distinct, boolean addSipLookupColumns) {
668458795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda        setTablesAndProjectionMapForData(qb, uri, projection, distinct, addSipLookupColumns, null);
668546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
668646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
668746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
668846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * @param usageType when non-null {@link Tables#DATA_USAGE_STAT} is joined with the specified
668946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * type.
669046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
669146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
669246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            String[] projection, boolean distinct, Integer usageType) {
669358795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda        setTablesAndProjectionMapForData(qb, uri, projection, distinct, false, usageType);
669458795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    }
669558795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda
669658795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
669758795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            String[] projection, boolean distinct, boolean addSipLookupColumns, Integer usageType) {
669882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
6699ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.DATA);
670082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sb.append(" data");
670182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
6702a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, RawContacts.CONTACT_ID);
6703a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
6704a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
6705a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
67063296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
670746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (usageType != null) {
670846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            appendDataUsageStatJoin(sb, usageType, DataColumns.CONCRETE_ID);
670946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
671046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
671182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
6712f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov
6713f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        boolean useDistinct = distinct
67145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                || !mDbHelper.get().isInProjection(projection, DISTINCT_DATA_PROHIBITING_COLUMNS);
6715f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setDistinct(useDistinct);
671658795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda
671758795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda        final ProjectionMap projectionMap;
671858795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda        if (addSipLookupColumns) {
671958795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            projectionMap = useDistinct
672058795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    ? sDistinctDataSipLookupProjectionMap : sDataSipLookupProjectionMap;
672158795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda        } else {
672258795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            projectionMap = useDistinct ? sDistinctDataProjectionMap : sDataProjectionMap;
672358795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda        }
672458795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda
672558795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda        qb.setProjectionMap(projectionMap);
6726f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        appendAccountFromParameter(qb, uri);
6727ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    }
6728ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
67290a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb,
67300a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String[] projection) {
67310a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        StringBuilder sb = new StringBuilder();
6732ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.DATA);
67330a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        sb.append(" data");
6734a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
6735a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
67360a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
6737a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
6738a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sStatusUpdatesProjectionMap);
6739a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6740a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
67413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void setTablesAndProjectionMapForStreamItems(SQLiteQueryBuilder qb) {
67429b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann        qb.setTables(Views.STREAM_ITEMS);
67433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        qb.setProjectionMap(sStreamItemsProjectionMap);
67443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
67453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
67463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void setTablesAndProjectionMapForStreamItemPhotos(SQLiteQueryBuilder qb) {
67471dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro        qb.setTables(Tables.PHOTO_FILES
67481dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + " JOIN " + Tables.STREAM_ITEM_PHOTOS + " ON ("
67491dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + StreamItemPhotosColumns.CONCRETE_PHOTO_FILE_ID + "="
67501dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + PhotoFilesColumns.CONCRETE_ID
67511dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + ") JOIN " + Tables.STREAM_ITEMS + " ON ("
67521dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "="
67530bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                + StreamItemsColumns.CONCRETE_ID + ")"
67540bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                + " JOIN " + Tables.RAW_CONTACTS + " ON ("
67550bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                + StreamItemsColumns.CONCRETE_RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID
67560bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                + ")");
67573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        qb.setProjectionMap(sStreamItemPhotosProjectionMap);
67583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
67593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
6760a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForEntities(SQLiteQueryBuilder qb, Uri uri,
6761a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection) {
6762a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
6763ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.ENTITIES);
6764a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(" data");
6765a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6766a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts.Entity.CONTACT_ID);
6767a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
6768a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, Contacts.Entity.DATA_ID);
6769a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, Contacts.Entity.DATA_ID);
6770a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6771a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
6772a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sEntityProjectionMap);
6773f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        appendAccountFromParameter(qb, uri);
6774a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6775a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6776a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactStatusUpdateJoin(StringBuilder sb, String[] projection,
6777a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String lastStatusUpdateIdColumn) {
67785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mDbHelper.get().isInProjection(projection,
6779a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS,
6780a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
6781a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
6782a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
6783a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP)) {
6784a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
6785a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    + ContactsStatusUpdatesColumns.ALIAS +
6786a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + lastStatusUpdateIdColumn + "="
6787a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
67880a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
6789a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
67900a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
6791a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataStatusUpdateJoin(StringBuilder sb, String[] projection,
6792a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
67935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mDbHelper.get().isInProjection(projection,
67940a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS,
67950a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_RES_PACKAGE,
67960a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_ICON,
67970a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_LABEL,
67980a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_TIMESTAMP)) {
67990a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
6800a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdatesColumns.CONCRETE_DATA_ID + "="
6801a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + dataIdColumn + ")");
68020a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
6803a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6804a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
680546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private void appendDataUsageStatJoin(StringBuilder sb, int usageType, String dataIdColumn) {
680646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        sb.append(" LEFT OUTER JOIN " + Tables.DATA_USAGE_STAT +
680746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "=" + dataIdColumn +
680846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                " AND " + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "=" + usageType + ")");
680946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
681046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
6811a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactPresenceJoin(StringBuilder sb, String[] projection,
6812a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn) {
68135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mDbHelper.get().isInProjection(projection,
6814a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_PRESENCE, Contacts.CONTACT_CHAT_CAPABILITY)) {
6815a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
6816a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + contactIdColumn + " = "
6817a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + AggregatedPresenceColumns.CONCRETE_CONTACT_ID + ")");
6818a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
6819a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6820a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6821a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataPresenceJoin(StringBuilder sb, String[] projection,
6822a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
68235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mDbHelper.get().isInProjection(projection, Data.PRESENCE, Data.CHAT_CAPABILITY)) {
6824a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
6825a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdates.DATA_ID + "=" + dataIdColumn + ")");
6826a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
6827a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6828a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
682924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private boolean appendLocalDirectorySelectionIfNeeded(SQLiteQueryBuilder qb, long directoryId) {
6830385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directoryId == Directory.DEFAULT) {
6831385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " IN " + Tables.DEFAULT_DIRECTORY);
683224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            return true;
6833385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directoryId == Directory.LOCAL_INVISIBLE){
6834385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " NOT IN " + Tables.DEFAULT_DIRECTORY);
683524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            return true;
683624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
683724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        return false;
683824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
683924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
6840f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro    private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) {
6841f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
6842f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
684343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        final String dataSet = getQueryParameter(uri, RawContacts.DATA_SET);
6844e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6845e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
6846e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
6847e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
68485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
6849fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
6850e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
6851e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6852e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
6853e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
6854e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
6855e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
685643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            String toAppend = RawContacts.ACCOUNT_NAME + "="
68574a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
68584a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
685943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + DatabaseUtils.sqlEscapeString(accountType);
6860f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            if (dataSet == null) {
6861f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                toAppend += " AND " + RawContacts.DATA_SET + " IS NULL";
6862f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            } else {
6863f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                toAppend += " AND " + RawContacts.DATA_SET + "=" +
6864f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                        DatabaseUtils.sqlEscapeString(dataSet);
686543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
686643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            qb.appendWhere(toAppend);
68674a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        } else {
68684a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere("1");
68694a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
68704a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
68714a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
6872e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    private String appendAccountToSelection(Uri uri, String selection) {
6873f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
6874f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
687543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        final String dataSet = getQueryParameter(uri, RawContacts.DATA_SET);
6876e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6877e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
6878e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
6879e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
68805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
6881fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
6882e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
6883e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6884e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
6885e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
6886e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
6887e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
6888e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "="
6889e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
6890e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + RawContacts.ACCOUNT_TYPE + "="
6891e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountType));
6892f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            if (dataSet == null) {
6893f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                selectionSb.append(" AND " + RawContacts.DATA_SET + " IS NULL");
6894f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            } else {
689543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                selectionSb.append(" AND " + RawContacts.DATA_SET + "=")
689643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        .append(DatabaseUtils.sqlEscapeString(dataSet));
689743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
6898e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (!TextUtils.isEmpty(selection)) {
6899e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(" AND (");
6900e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(selection);
6901e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(')');
6902e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
6903e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selectionSb.toString();
6904e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } else {
6905e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selection;
6906e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
6907e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    }
6908e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
69097e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
6910c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
6911c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *
6912c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
6913c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
6914c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     */
6915f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private String getLimit(Uri uri) {
69162e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limitParam = getQueryParameter(uri, ContactsContract.LIMIT_PARAM_KEY);
6917c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limitParam == null) {
6918c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
6919c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
6920c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // make sure that the limit is a non-negative integer
6921c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        try {
6922c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int l = Integer.parseInt(limitParam);
6923c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (l < 0) {
6924c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
6925c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return null;
6926c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
6927c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return String.valueOf(l);
6928c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        } catch (NumberFormatException ex) {
6929c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
6930c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
6931c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
6932c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
6933c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
6934b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    @Override
6935f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
6936f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (mode.equals("r")) {
6937f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            waitForAccess(mReadAccessLatch);
6938f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } else {
6939f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            waitForAccess(mWriteAccessLatch);
6940f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
69415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDb(uri)) {
69425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToProfileMode();
69435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return mProfileProvider.openAssetFile(uri, mode);
69445d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } else {
69455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToContactMode();
69465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return openAssetFileLocal(uri, mode);
69475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
69485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
69495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
69505d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    public AssetFileDescriptor openAssetFileLocal(Uri uri, String mode)
69515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            throws FileNotFoundException {
69525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
69535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
69545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
6955078f588cef389358adabc579de00747878f3c108Dave Santoro            if (mode.equals("r")) {
6956078f588cef389358adabc579de00747878f3c108Dave Santoro                mActiveDb.set(mContactsHelper.getReadableDatabase());
6957078f588cef389358adabc579de00747878f3c108Dave Santoro            } else {
6958078f588cef389358adabc579de00747878f3c108Dave Santoro                mActiveDb.set(mContactsHelper.getWritableDatabase());
6959078f588cef389358adabc579de00747878f3c108Dave Santoro            }
69605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
6961415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
6962b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        int match = sUriMatcher.match(uri);
6963b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        switch (match) {
6964a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
6965bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                long contactId = Long.parseLong(uri.getPathSegments().get(1));
69665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return openPhotoAssetFile(mActiveDb.get(), uri, mode,
696724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        Data._ID + "=" + Contacts.PHOTO_ID + " AND " +
696824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                                RawContacts.CONTACT_ID + "=?",
6969bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        new String[]{String.valueOf(contactId)});
6970e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            }
6971b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
6972f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_ID_DISPLAY_PHOTO: {
6973f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
6974f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
6975f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Display photos retrieved by contact ID can only be read.");
6976f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6977f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long contactId = Long.parseLong(uri.getPathSegments().get(1));
69785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = mActiveDb.get().query(Tables.CONTACTS,
6979f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{Contacts.PHOTO_FILE_ID},
6980f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        Contacts._ID + "=?", new String[]{String.valueOf(contactId)},
6981f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        null, null, null);
6982f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
698385077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    if (c.moveToFirst()) {
698485077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        long photoFileId = c.getLong(0);
698585077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        return openDisplayPhotoForRead(photoFileId);
698685077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    } else {
698785077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        // No contact for this ID.
698885077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        throw new FileNotFoundException(uri.toString());
698985077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    }
699085077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                } finally {
699185077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    c.close();
699285077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                }
699385077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro            }
699485077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro
699585077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro            case PROFILE_DISPLAY_PHOTO: {
699685077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                if (!mode.equals("r")) {
699785077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    throw new IllegalArgumentException(
699885077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                            "Display photos retrieved by contact ID can only be read.");
699985077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                }
700085077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                Cursor c = mActiveDb.get().query(Tables.CONTACTS,
700185077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        new String[]{Contacts.PHOTO_FILE_ID}, null, null, null, null, null);
700285077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                try {
700385077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    if (c.moveToFirst()) {
700485077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        long photoFileId = c.getLong(0);
700585077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        return openDisplayPhotoForRead(photoFileId);
700685077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    } else {
700785077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        // No profile record.
700885077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        throw new FileNotFoundException(uri.toString());
700985077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    }
7010f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
7011f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
7012f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7013f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
7014f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7015bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_PHOTO:
7016bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_ID_PHOTO:
7017f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_DISPLAY_PHOTO:
7018f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_ID_DISPLAY_PHOTO: {
7019f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
7020f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
7021bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                            "Photos retrieved by contact lookup key can only be read.");
7022f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7023f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                List<String> pathSegments = uri.getPathSegments();
7024f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                int segmentCount = pathSegments.size();
7025f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (segmentCount < 4) {
70265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
7027f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Missing a lookup key", uri));
7028f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7029bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro
7030bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                boolean forDisplayPhoto = (match == CONTACTS_LOOKUP_ID_DISPLAY_PHOTO
7031bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        || match == CONTACTS_LOOKUP_DISPLAY_PHOTO);
7032f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                String lookupKey = pathSegments.get(2);
7033bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                String[] projection = new String[]{Contacts.PHOTO_ID, Contacts.PHOTO_FILE_ID};
7034f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (segmentCount == 5) {
7035f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    long contactId = Long.parseLong(pathSegments.get(3));
7036f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
7037f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
70385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
7039f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            projection, null, null, null, null, null,
7040f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
7041f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (c != null) {
7042f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        try {
7043f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            c.moveToFirst();
7044bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                            if (forDisplayPhoto) {
7045bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                long photoFileId =
7046bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                        c.getLong(c.getColumnIndex(Contacts.PHOTO_FILE_ID));
7047bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                return openDisplayPhotoForRead(photoFileId);
7048bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                            } else {
7049bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                long photoId = c.getLong(c.getColumnIndex(Contacts.PHOTO_ID));
7050bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                return openPhotoAssetFile(mActiveDb.get(), uri, mode,
7051bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                        Data._ID + "=?", new String[]{String.valueOf(photoId)});
7052bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                            }
7053f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        } finally {
7054f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            c.close();
7055f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
7056f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
7057f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7058f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7059f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
7060f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                setTablesAndProjectionMapForContacts(qb, uri, projection);
70615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
70625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = qb.query(mActiveDb.get(), projection, Contacts._ID + "=?",
7063f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{String.valueOf(contactId)}, null, null, null);
7064f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
7065f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.moveToFirst();
7066bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    if (forDisplayPhoto) {
7067bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        long photoFileId = c.getLong(c.getColumnIndex(Contacts.PHOTO_FILE_ID));
7068bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        return openDisplayPhotoForRead(photoFileId);
7069bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    } else {
7070bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        long photoId = c.getLong(c.getColumnIndex(Contacts.PHOTO_ID));
7071bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        return openPhotoAssetFile(mActiveDb.get(), uri, mode,
7072bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                Data._ID + "=?", new String[]{String.valueOf(photoId)});
7073bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    }
7074f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
7075f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
7076f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7077f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
7078f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7079f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case RAW_CONTACTS_ID_DISPLAY_PHOTO: {
7080f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
7081f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                boolean writeable = !mode.equals("r");
7082f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7083f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Find the primary photo data record for this raw contact.
7084f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
7085f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                String[] projection = new String[]{Data._ID, Photo.PHOTO_FILE_ID};
7086f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
70877cf50494501938f175d288077145acf49da8f171Daniel Lehmann                long photoMimetypeId = mDbHelper.get().getMimeTypeId(Photo.CONTENT_ITEM_TYPE);
70885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = qb.query(mActiveDb.get(), projection,
70897cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        Data.RAW_CONTACT_ID + "=? AND " + DataColumns.MIMETYPE_ID + "=?",
70907cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        new String[]{String.valueOf(rawContactId), String.valueOf(photoMimetypeId)},
7091f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        null, null, Data.IS_PRIMARY + " DESC");
7092f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long dataId = 0;
7093f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long photoFileId = 0;
7094f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
7095f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (c.getCount() >= 1) {
7096f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        c.moveToFirst();
7097f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        dataId = c.getLong(0);
7098f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        photoFileId = c.getLong(1);
7099f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
7100f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
7101f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
7102f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7103f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7104f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // If writeable, open a writeable file descriptor that we can monitor.
7105f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // When the caller finishes writing content, we'll process the photo and
7106f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // update the data record.
7107f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (writeable) {
7108f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForWrite(rawContactId, dataId, uri, mode);
7109f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } else {
7110f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForRead(photoFileId);
7111f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7112f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
7113f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7114f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case DISPLAY_PHOTO: {
7115f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long photoFileId = ContentUris.parseId(uri);
7116f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
7117f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
7118f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Display photos retrieved by key can only be read.");
7119f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7120f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                return openDisplayPhotoForRead(photoFileId);
7121f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
7122f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7123e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            case DATA_ID: {
712424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long dataId = Long.parseLong(uri.getPathSegments().get(1));
71257cf50494501938f175d288077145acf49da8f171Daniel Lehmann                long photoMimetypeId = mDbHelper.get().getMimeTypeId(Photo.CONTENT_ITEM_TYPE);
71265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return openPhotoAssetFile(mActiveDb.get(), uri, mode,
71277cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        Data._ID + "=? AND " + DataColumns.MIMETYPE_ID + "=" + photoMimetypeId,
712824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        new String[]{String.valueOf(dataId)});
7129d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
7130d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7131fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            case PROFILE_AS_VCARD: {
7132fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // When opening a contact as file, we pass back contents as a
7133fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // vCard-encoded stream. We build into a local buffer first,
7134fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // then pipe into MemoryFile once the exact size is known.
7135fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
7136fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(uri, localStream, null, null);
7137fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                return buildAssetFileDescriptor(localStream);
7138fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            }
713942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
7140fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            case CONTACTS_AS_VCARD: {
714142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // When opening a contact as file, we pass back contents as a
714242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // vCard-encoded stream. We build into a local buffer first,
714342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // then pipe into MemoryFile once the exact size is known.
714442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
7145fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(uri, localStream, null, null);
7146f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
714742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
714842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
714942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
715042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKeys = uri.getPathSegments().get(2);
715142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String[] loopupKeyList = lookupKeys.split(":");
715242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final StringBuilder inBuilder = new StringBuilder();
7153fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                Uri queryUri = Contacts.CONTENT_URI;
715442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                int index = 0;
7155fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen
7156d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // SQLite has limits on how many parameters can be used
7157d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // so the IDs are concatenated to a query string here instead
715842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                for (String lookupKey : loopupKeyList) {
715942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    if (index == 0) {
7160d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append("(");
716142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    } else {
7162d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append(",");
716342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    }
71645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    // TODO: Figure out what to do if the profile contact is in the list.
71655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
716624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    inBuilder.append(contactId);
716742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    index++;
716842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                }
716942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                inBuilder.append(')');
717042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + " IN " + inBuilder.toString();
7171d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7172d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // When opening a contact as file, we pass back contents as a
7173d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // vCard-encoded stream. We build into a local buffer first,
7174d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // then pipe into MemoryFile once the exact size is known.
7175d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
7176fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(queryUri, localStream, selection, null);
7177f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
7178d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
7179b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
7180b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            default:
71815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                throw new FileNotFoundException(mDbHelper.get().exceptionMessage(
71825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        "File does not exist", uri));
7183b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        }
7184b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
7185b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
7186afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro    private AssetFileDescriptor openPhotoAssetFile(SQLiteDatabase db, Uri uri, String mode,
7187afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro            String selection, String[] selectionArgs)
7188e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throws FileNotFoundException {
7189e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        if (!"r".equals(mode)) {
71905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            throw new FileNotFoundException(mDbHelper.get().exceptionMessage("Mode " + mode
7191e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                    + " not supported.", uri));
7192e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        }
7193e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
7194e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        String sql =
7195ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                "SELECT " + Photo.PHOTO + " FROM " + Views.DATA +
7196e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                " WHERE " + selection;
719708ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        try {
7198f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
7199f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    DatabaseUtils.blobFileDescriptorForQuery(db, sql, selectionArgs));
720008ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        } catch (SQLiteDoneException e) {
720108ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            // this will happen if the DB query returns no rows (i.e. contact does not exist)
720208ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            throw new FileNotFoundException(uri.toString());
720308ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        }
7204e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov    }
7205e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
7206f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
7207f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Opens a display photo from the photo store for reading.
7208f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param photoFileId The display photo file ID
7209f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @return An asset file descriptor that allows the file to be read.
7210f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @throws FileNotFoundException If no photo file for the given ID exists.
7211f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
7212f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private AssetFileDescriptor openDisplayPhotoForRead(long photoFileId)
7213f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            throws FileNotFoundException {
72145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        PhotoStore.Entry entry = mPhotoStore.get().get(photoFileId);
7215f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (entry != null) {
7216d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro            try {
7217d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                return makeAssetFileDescriptor(
7218d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        ParcelFileDescriptor.open(new File(entry.path),
7219d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                                ParcelFileDescriptor.MODE_READ_ONLY),
7220d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        entry.size);
7221d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro            } catch (FileNotFoundException fnfe) {
7222d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
7223d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                throw fnfe;
7224d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro            }
7225f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } else {
7226f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
7227f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            throw new FileNotFoundException("No photo file found for ID " + photoFileId);
7228f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
7229f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
7230f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7231f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
7232f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Opens a file descriptor for a photo to be written.  When the caller completes writing
7233f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * to the file (closing the output stream), the image will be parsed out and processed.
7234f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * If processing succeeds, the given raw contact ID's primary photo record will be
7235f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * populated with the inserted image (if no primary photo record exists, the data ID can
7236f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * be left as 0, and a new data record will be inserted).
7237f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param rawContactId Raw contact ID this photo entry should be associated with.
7238f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param dataId Data ID for a photo mimetype that will be updated with the inserted
7239f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     image.  May be set to 0, in which case the inserted image will trigger creation
7240f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     of a new primary photo image data row for the raw contact.
7241f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param uri The URI being used to access this file.
7242f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param mode Read/write mode string.
7243f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @return An asset file descriptor the caller can use to write an image file for the
7244f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     raw contact.
7245f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
7246f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private AssetFileDescriptor openDisplayPhotoForWrite(long rawContactId, long dataId, Uri uri,
7247f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            String mode) {
7248f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        try {
7249c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            ParcelFileDescriptor[] pipeFds = ParcelFileDescriptor.createPipe();
7250c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            PipeMonitor pipeMonitor = new PipeMonitor(rawContactId, dataId, pipeFds[0]);
7251c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            pipeMonitor.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Object[]) null);
7252c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            return new AssetFileDescriptor(pipeFds[1], 0, AssetFileDescriptor.UNKNOWN_LENGTH);
7253f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } catch (IOException ioe) {
7254f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            Log.e(TAG, "Could not create temp image file in mode " + mode);
7255f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            return null;
7256f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
7257f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
7258f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7259f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
7260c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro     * Async task that monitors the given file descriptor (the read end of a pipe) for
7261c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro     * the writer finishing.  If the data from the pipe contains a valid image, the image
7262c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro     * is either inserted into the given raw contact or updated in the given data row.
7263f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
7264c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro    private class PipeMonitor extends AsyncTask<Object, Object, Object> {
7265c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro        private final ParcelFileDescriptor mDescriptor;
7266f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        private final long mRawContactId;
7267f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        private final long mDataId;
7268c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro        private PipeMonitor(long rawContactId, long dataId, ParcelFileDescriptor descriptor) {
7269f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            mRawContactId = rawContactId;
7270f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            mDataId = dataId;
7271c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            mDescriptor = descriptor;
7272f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
7273f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7274f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        @Override
7275c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro        protected Object doInBackground(Object... params) {
7276c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            AutoCloseInputStream is = new AutoCloseInputStream(mDescriptor);
7277f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            try {
7278c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                Bitmap b = BitmapFactory.decodeStream(is);
7279f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (b != null) {
7280fa4db3db4146a26f154ef2e89352ad70a5415b8eDaniel Lehmann                    waitForAccess(mWriteAccessLatch);
7281f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    PhotoProcessor processor = new PhotoProcessor(b, mMaxDisplayPhotoDim,
7282f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            mMaxThumbnailPhotoDim);
7283f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7284f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    // Store the compressed photo in the photo store.
72855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    PhotoStore photoStore = ContactsContract.isProfileId(mRawContactId)
72865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            ? mProfilePhotoStore
72875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            : mContactsPhotoStore;
72885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    long photoFileId = photoStore.insert(processor);
7289f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7290c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                    // Depending on whether we already had a data row to attach the photo
7291c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                    // to, do an update or insert.
7292f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (mDataId != 0) {
7293f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Update the data record with the new photo.
7294f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        ContentValues updateValues = new ContentValues();
7295f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7296f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Signal that photo processing has already been handled.
7297f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        updateValues.put(DataRowHandlerForPhoto.SKIP_PROCESSING_KEY, true);
7298f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7299f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        if (photoFileId != 0) {
7300f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            updateValues.put(Photo.PHOTO_FILE_ID, photoFileId);
7301f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
7302f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        updateValues.put(Photo.PHOTO, processor.getThumbnailPhotoBytes());
7303c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                        update(ContentUris.withAppendedId(Data.CONTENT_URI, mDataId),
7304c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                                updateValues, null, null);
7305f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    } else {
7306f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Insert a new primary data record with the photo.
7307f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        ContentValues insertValues = new ContentValues();
7308f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7309f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Signal that photo processing has already been handled.
7310f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(DataRowHandlerForPhoto.SKIP_PROCESSING_KEY, true);
7311f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7312f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
7313f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Data.IS_PRIMARY, 1);
7314f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        if (photoFileId != 0) {
7315f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            insertValues.put(Photo.PHOTO_FILE_ID, photoFileId);
7316f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
7317f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Photo.PHOTO, processor.getThumbnailPhotoBytes());
7318f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insert(RawContacts.CONTENT_URI.buildUpon()
7319f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                .appendPath(String.valueOf(mRawContactId))
7320f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                .appendPath(RawContacts.Data.CONTENT_DIRECTORY).build(),
7321f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                insertValues);
7322f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
7323c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro
7324f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7325c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            } catch (IOException e) {
7326c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                throw new RuntimeException(e);
7327f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
7328c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            return null;
7329f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
7330f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
7331f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7332d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String CONTACT_MEMORY_FILE_NAME = "contactAssetFile";
7333d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7334d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
7335f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert     * Returns an {@link AssetFileDescriptor} backed by the
7336d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * contents of the given {@link ByteArrayOutputStream}.
7337d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
7338f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor buildAssetFileDescriptor(ByteArrayOutputStream stream) {
7339d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        try {
7340d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            stream.flush();
7341d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7342d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final byte[] byteData = stream.toByteArray();
7343d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7344f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
7345f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    ParcelFileDescriptor.fromData(byteData, CONTACT_MEMORY_FILE_NAME),
7346f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    byteData.length);
7347d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        } catch (IOException e) {
7348ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            Log.w(TAG, "Problem writing stream into an ParcelFileDescriptor: " + e.toString());
7349ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            return null;
7350d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
7351d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
7352d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7353f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd) {
7354f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return makeAssetFileDescriptor(fd, AssetFileDescriptor.UNKNOWN_LENGTH);
7355f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
7356f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
7357f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd, long length) {
7358f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return fd != null ? new AssetFileDescriptor(fd, 0, length) : null;
7359f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
7360f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
7361d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
7362d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Output {@link RawContacts} matching the requested selection in the vCard
7363d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * format to the given {@link OutputStream}. This method returns silently if
7364d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * any errors encountered.
7365d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
7366fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen    private void outputRawContactsAsVCard(Uri uri, OutputStream stream,
7367fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            String selection, String[] selectionArgs) {
7368d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final Context context = this.getContext();
7369dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen        int vcardconfig = VCardConfig.VCARD_TYPE_DEFAULT;
7370fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen        if(uri.getBooleanQueryParameter(
7371fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                Contacts.QUERY_PARAMETER_VCARD_NO_PHOTO, false)) {
7372dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen            vcardconfig |= VCardConfig.FLAG_REFRAIN_IMAGE_EXPORT;
7373dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen        }
73747a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        final VCardComposer composer =
7375dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen                new VCardComposer(context, vcardconfig, false);
7376108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        Writer writer = null;
73773711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen        final Uri rawContactsUri;
73783711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen        if (mapsToProfileDb(uri)) {
737982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // Pre-authorize the URI, since the caller would have already gone through the
738082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // permission check to get here, but the pre-authorization at the top level wouldn't
738182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // carry over to the raw contact.
738282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            rawContactsUri = preAuthorizeUri(RawContactsEntity.PROFILE_CONTENT_URI);
73833711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen        } else {
73843711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen            rawContactsUri = RawContactsEntity.CONTENT_URI;
73853711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen        }
7386108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        try {
7387108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            writer = new BufferedWriter(new OutputStreamWriter(stream));
73883711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen            if (!composer.init(uri, selection, selectionArgs, null, rawContactsUri)) {
7389108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                Log.w(TAG, "Failed to init VCardComposer");
7390108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                return;
7391108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            }
7392d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7393108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            while (!composer.isAfterLast()) {
7394108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                writer.write(composer.createOneEntry());
7395108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            }
7396108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        } catch (IOException e) {
7397108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            Log.e(TAG, "IOException: " + e);
7398108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        } finally {
7399108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            composer.terminate();
7400108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            if (writer != null) {
7401108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                try {
7402108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                    writer.close();
7403108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                } catch (IOException e) {
7404108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                    Log.w(TAG, "IOException during closing output stream: " + e);
7405108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                }
7406d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
7407d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
7408d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
7409b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
74104f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
74114f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
7412415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
7413415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov        waitForAccess(mReadAccessLatch);
7414415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
7415a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
74164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
7417b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS:
7418be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return Contacts.CONTENT_TYPE;
74192d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill            case CONTACTS_LOOKUP:
7420b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_ID:
7421b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
742224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE:
7423b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_ITEM_TYPE;
7424f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD:
742542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD:
742624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD:
7427f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                return Contacts.CONTENT_VCARD_TYPE;
7428f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            case CONTACTS_ID_PHOTO:
7429bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_PHOTO:
7430bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_ID_PHOTO:
7431f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_ID_DISPLAY_PHOTO:
7432f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_DISPLAY_PHOTO:
7433f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_ID_DISPLAY_PHOTO:
7434f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case RAW_CONTACTS_ID_DISPLAY_PHOTO:
7435f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case DISPLAY_PHOTO:
7436f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                return "image/jpeg";
7437b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS:
743824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS:
7439be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return RawContacts.CONTENT_TYPE;
7440b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS_ID:
744124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID:
7442b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return RawContacts.CONTENT_ITEM_TYPE;
7443f481f22a9323fe338672f99b88b26c5f0725cd42David Brown            case DATA:
744424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA:
7445f481f22a9323fe338672f99b88b26c5f0725cd42David Brown                return Data.CONTENT_TYPE;
7446508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
74475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long id = ContentUris.parseId(uri);
74485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                if (ContactsContract.isProfileId(id)) {
74495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    return mProfileHelper.getDataMimeType(id);
74505d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                } else {
74515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    return mContactsHelper.getDataMimeType(id);
74525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                }
745348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES:
745448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_TYPE;
745548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
745648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_ITEM_TYPE;
74579005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov            case PHONE_LOOKUP:
74589005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov                return PhoneLookup.CONTENT_TYPE;
745948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS:
746048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_TYPE;
746148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
746248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_ITEM_TYPE;
746348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS:
746448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_TYPE;
746548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID:
746648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_ITEM_TYPE;
7467b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
7468b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_TYPE;
7469b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
7470b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_ITEM_TYPE;
7471b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case SETTINGS:
7472b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Settings.CONTENT_TYPE;
7473b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
7474b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_TYPE;
7475c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS:
7476c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SUGGEST_MIME_TYPE;
7477c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT:
7478c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
7479d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES:
7480d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_TYPE;
7481d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID:
7482d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_ITEM_TYPE;
7483af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS:
7484af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                return StreamItems.CONTENT_TYPE;
7485af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS_ID:
7486af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                return StreamItems.CONTENT_ITEM_TYPE;
7487af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS_ID_PHOTOS:
7488af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                return StreamItems.StreamItemPhotos.CONTENT_TYPE;
7489af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS_ID_PHOTOS_ID:
7490af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                return StreamItems.StreamItemPhotos.CONTENT_ITEM_TYPE;
7491af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS_PHOTOS:
7492af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                throw new UnsupportedOperationException("Not supported for write-only URI " + uri);
749361efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov            default:
749461efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov                return mLegacyApiSupport.getType(uri);
74954f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
74964f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
74977e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
749809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    public String[] getDefaultProjection(Uri uri) {
749909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        final int match = sUriMatcher.match(uri);
750009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        switch (match) {
750109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS:
750209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP:
750309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_ID:
750409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
750509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
750624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE:
750709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsProjectionMap.getColumnNames();
750809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
75098727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov            case CONTACTS_ID_ENTITIES:
751024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_ENTITIES:
75118727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov                return sEntityProjectionMap.getColumnNames();
75128727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov
751309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_VCARD:
751409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_MULTI_VCARD:
751524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD:
751609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsVCardProjectionMap.getColumnNames();
751709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
751809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS:
751909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS_ID:
752024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS:
752124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID:
752209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sRawContactsProjectionMap.getColumnNames();
752309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
752409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DATA_ID:
752509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES:
752609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES_ID:
752709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS:
752809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS_ID:
752909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS:
753009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS_ID:
753124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA:
753209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDataProjectionMap.getColumnNames();
753309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
753409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONE_LOOKUP:
753509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sPhoneLookupProjectionMap.getColumnNames();
753609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
753709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
753809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
753909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sAggregationExceptionsProjectionMap.getColumnNames();
754009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
754109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case SETTINGS:
754209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sSettingsProjectionMap.getColumnNames();
754309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
754409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES:
754509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES_ID:
754609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDirectoryProjectionMap.getColumnNames();
754709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
754809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            default:
754909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return null;
755009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
755109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    }
755209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
7553f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private class StructuredNameLookupBuilder extends NameLookupBuilder {
7554f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
7555f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public StructuredNameLookupBuilder(NameSplitter splitter) {
7556f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            super(splitter);
7557f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
7558f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
7559f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
7560f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
7561f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name) {
75625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mDbHelper.get().insertNameLookup(rawContactId, dataId, lookupType, name);
7563f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
7564f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
7565f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
7566f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected String[] getCommonNicknameClusters(String normalizedName) {
7567d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov            return mCommonNicknameCache.getCommonNicknameClusters(normalizedName);
7568f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
7569f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
7570f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
75712d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov    public void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
7572d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
7573d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + RawContacts.CONTACT_ID +
7574d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.RAW_CONTACTS +
7575d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " JOIN " + Tables.NAME_LOOKUP +
7576d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " ON(" + RawContactsColumns.CONCRETE_ID + "="
7577d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
7578d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE normalized_name GLOB '");
7579e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
7580916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE +
7581916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " IN(" + CONTACT_LOOKUP_NAME_TYPES + "))");
7582e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    }
7583e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
75849a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    public boolean isPhoneNumber(String filter) {
75859a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        boolean atLeastOneDigit = false;
75869a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        int len = filter.length();
75879a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        for (int i = 0; i < len; i++) {
75889a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            char c = filter.charAt(i);
75899a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            if (c >= '0' && c <= '9') {
75909a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                atLeastOneDigit = true;
75919a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            } else if (c != '*' && c != '#' && c != '+' && c != 'N' && c != '.' && c != ';'
75929a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                    && c != '-' && c != '(' && c != ')' && c != ' ') {
75939a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                return false;
75949a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            }
75959a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
75969a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return atLeastOneDigit;
75979a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
75989a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
75994a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /**
76007a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * Takes components of a name from the query parameters and returns a cursor with those
76017a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * components as well as all missing components.  There is no database activity involved
76027a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * in this so the call can be made on the UI thread.
76037a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     */
76047a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private Cursor completeName(Uri uri, String[] projection) {
76057a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        if (projection == null) {
76067a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            projection = sDataProjectionMap.getColumnNames();
76077a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
76087a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
76097a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        ContentValues values = new ContentValues();
7610f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        DataRowHandlerForStructuredName handler = (DataRowHandlerForStructuredName)
7611f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                getDataRowHandler(StructuredName.CONTENT_ITEM_TYPE);
76127a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
76137a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        copyQueryParamsToContentValues(values, uri,
76147a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.DISPLAY_NAME,
76157a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PREFIX,
76167a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.GIVEN_NAME,
76177a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.MIDDLE_NAME,
76187a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.FAMILY_NAME,
76197a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.SUFFIX,
76207a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_NAME,
76217a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_FAMILY_NAME,
76227a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_MIDDLE_NAME,
76237a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_GIVEN_NAME
76247a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        );
76257a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
76267a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        handler.fixStructuredNameComponents(values, values);
76277a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
76287a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
76297a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        Object[] row = new Object[projection.length];
76307a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
76317a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            row[i] = values.get(projection[i]);
76327a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
76337a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        cursor.addRow(row);
76347a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        return cursor;
76357a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
76367a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
76377a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private void copyQueryParamsToContentValues(ContentValues values, Uri uri, String... columns) {
76387a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (String column : columns) {
76397a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            String param = uri.getQueryParameter(column);
76407a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            if (param != null) {
76417a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                values.put(column, param);
76427a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
76437a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
76447a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
76457a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
76467a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
76477a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    /**
76484a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     * Inserts an argument at the beginning of the selection arg list.
76494a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     */
76504a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private String[] insertSelectionArg(String[] selectionArgs, String arg) {
7651b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
7652b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
7653b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
7654b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
7655b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
76564a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            newSelectionArgs[0] = arg;
76574a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length);
7658b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
7659b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
7660b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
7661caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
76625e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private String[] appendProjectionArg(String[] projection, String arg) {
76635e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        if (projection == null) {
76645e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            return null;
76655e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        }
76665e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        final int length = projection.length;
76675e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        String[] newProjection = new String[length + 1];
76685e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        System.arraycopy(projection, 0, newProjection, 0, length);
76695e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        newProjection[length] = arg;
76705e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        return newProjection;
76715e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    }
76725e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
7673caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    protected Account getDefaultAccount() {
7674caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        AccountManager accountManager = AccountManager.get(getContext());
7675caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        try {
76765f1f4a062ac34d75d2dbf586702cbeb121cf09caDmitri Plotnikov            Account[] accounts = accountManager.getAccountsByType(DEFAULT_ACCOUNT_TYPE);
7677caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            if (accounts != null && accounts.length > 0) {
7678caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov                return accounts[0];
7679caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            }
7680caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        } catch (Throwable e) {
76816f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov            Log.e(TAG, "Cannot determine the default account for contacts compatibility", e);
7682caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        }
76836f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov        return null;
7684caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    }
7685f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
768673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    /**
768743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * Returns true if the specified account type and data set is writable.
768873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     */
768943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    protected boolean isWritableAccountWithDataSet(String accountTypeAndDataSet) {
769043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        if (accountTypeAndDataSet == null) {
7691bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov            return true;
7692bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        }
7693bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov
769443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        Boolean writable = mAccountWritability.get(accountTypeAndDataSet);
769573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable != null) {
769673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            return writable;
769773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
769873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
7699627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        IContentService contentService = ContentResolver.getContentService();
7700627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
770143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // TODO(dsantoro): Need to update this logic to allow for sub-accounts.
7702627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (SyncAdapterType sync : contentService.getSyncAdapterTypes()) {
7703627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (ContactsContract.AUTHORITY.equals(sync.authority) &&
770443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        accountTypeAndDataSet.equals(sync.accountType)) {
770573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    writable = sync.supportsUploading();
770673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    break;
7707627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
7708627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
7709627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } catch (RemoteException e) {
7710627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            Log.e(TAG, "Could not acquire sync adapter types");
7711627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
771273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
771373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable == null) {
771473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            writable = false;
771573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
771673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
771743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        mAccountWritability.put(accountTypeAndDataSet, writable);
771873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        return writable;
7719627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
7720b4e61e064b59e8076df81b061add9fb358fd2ed9Dmitri Plotnikov
7721d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
7722f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static boolean readBooleanQueryParameter(Uri uri, String parameter,
7723f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean defaultValue) {
7724f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7725f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        // Manually parse the query, which is much faster than calling uri.getQueryParameter
7726f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
7727f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
7728f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
7729f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7730f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7731f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = query.indexOf(parameter);
7732f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (index == -1) {
7733f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
7734f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7735f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7736f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        index += parameter.length();
7737f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7738f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return !matchQueryParameter(query, index, "=0", false)
7739f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && !matchQueryParameter(query, index, "=false", true);
7740f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
7741f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7742f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private static boolean matchQueryParameter(String query, int index, String value,
7743f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean ignoreCase) {
7744f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int length = value.length();
7745f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return query.regionMatches(ignoreCase, index, value, 0, length)
7746f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && (query.length() == index + length || query.charAt(index + length) == '&');
7747f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
7748f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7749f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /**
7750f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * A fast re-implementation of {@link Uri#getQueryParameter}
7751f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     */
7752f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static String getQueryParameter(Uri uri, String parameter) {
7753f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
7754f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
7755f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return null;
7756f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7757f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7758f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int queryLength = query.length();
7759f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int parameterLength = parameter.length();
7760f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7761f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String value;
7762f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = 0;
7763f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        while (true) {
7764f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index = query.indexOf(parameter, index);
7765f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (index == -1) {
7766f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
77675fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            }
77685fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa
77695fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            // Should match against the whole parameter instead of its suffix.
77705fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            // e.g. The parameter "param" must not be found in "some_param=val".
77715fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            if (index > 0) {
77725fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                char prevChar = query.charAt(index - 1);
77735fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                if (prevChar != '?' && prevChar != '&') {
77745fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    // With "some_param=val1&param=val2", we should find second "param" occurrence.
77755fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    index += parameterLength;
77765fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    continue;
77775fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                }
7778f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
7779f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7780f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index += parameterLength;
7781f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7782f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (queryLength == index) {
7783f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
7784f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
7785f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7786f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (query.charAt(index) == '=') {
7787f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                index++;
7788f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                break;
7789f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
7790f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7791f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7792f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int ampIndex = query.indexOf('&', index);
7793f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (ampIndex == -1) {
7794f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index);
7795f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        } else {
7796f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index, ampIndex);
7797f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7798f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7799f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return Uri.decode(value);
7800f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
78015dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
78020dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    protected boolean isAggregationUpgradeNeeded() {
78030dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
78040dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            return false;
78050dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
78060dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
78075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int version = Integer.parseInt(mContactsHelper.getProperty(
78085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                PROPERTY_AGGREGATION_ALGORITHM, "1"));
78090dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        return version < PROPERTY_AGGREGATION_ALGORITHM_VERSION;
78100dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
78110dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
7812bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void upgradeAggregationAlgorithmInBackground() {
78130dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // This upgrade will affect very few contacts, so it can be performed on the
78140dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // main thread during the initial boot after an OTA
78150dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
78160dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        Log.i(TAG, "Upgrading aggregation algorithm");
78170dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int count = 0;
78180dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        long start = SystemClock.currentThreadTimeMillis();
78195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        SQLiteDatabase db = null;
78200dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        try {
78215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToContactMode();
78225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db = mContactsHelper.getWritableDatabase();
78235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.set(db);
78245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db.beginTransaction();
78255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            Cursor cursor = db.query(true,
78260dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    Tables.RAW_CONTACTS + " r1 JOIN " + Tables.RAW_CONTACTS + " r2",
78270dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    new String[]{"r1." + RawContacts._ID},
78280dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    "r1." + RawContacts._ID + "!=r2." + RawContacts._ID +
78290dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.CONTACT_ID + "=r2." + RawContacts.CONTACT_ID +
78300dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.ACCOUNT_NAME + "=r2." + RawContacts.ACCOUNT_NAME +
783143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    " AND r1." + RawContacts.ACCOUNT_TYPE + "=r2." + RawContacts.ACCOUNT_TYPE +
783243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    " AND r1." + RawContacts.DATA_SET + "=r2." + RawContacts.DATA_SET,
78330dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    null, null, null, null, null);
78340dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            try {
78350dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                while (cursor.moveToNext()) {
78360dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    long rawContactId = cursor.getLong(0);
78370dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId,
78380dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                            RawContacts.AGGREGATION_MODE_DEFAULT, true);
78390dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    count++;
78400dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                }
78410dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            } finally {
78420dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                cursor.close();
78430dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            }
78445d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mContactAggregator.aggregateInTransaction(mTransactionContext.get(), db);
7845bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            updateSearchIndexInTransaction();
78465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db.setTransactionSuccessful();
78475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mContactsHelper.setProperty(PROPERTY_AGGREGATION_ALGORITHM,
78480dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    String.valueOf(PROPERTY_AGGREGATION_ALGORITHM_VERSION));
78490dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        } finally {
78505d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (db != null) {
78515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                db.endTransaction();
78525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
78530dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            long end = SystemClock.currentThreadTimeMillis();
78540dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            Log.i(TAG, "Aggregation algorithm upgraded for " + count
78550dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    + " contacts, in " + (end - start) + "ms");
78560dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
78570dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
78589a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
78599a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    /* Visible for testing */
78609a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    boolean isPhone() {
78619a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (!sIsPhoneInitialized) {
78629a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sIsPhone = new TelephonyManager(getContext()).isVoiceCapable();
78639a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sIsPhoneInitialized = true;
78649a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
78659a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return sIsPhone;
78669a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
786746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
786846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private boolean handleDataUsageFeedback(Uri uri) {
786946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final long currentTimeMillis = System.currentTimeMillis();
787046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String usageType = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
787146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] ids = uri.getLastPathSegment().trim().split(",");
787246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final ArrayList<Long> dataIds = new ArrayList<Long>();
787346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
787446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        for (String id : ids) {
787546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            dataIds.add(Long.valueOf(id));
787646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
787746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final boolean successful;
787846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (TextUtils.isEmpty(usageType)) {
787946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            Log.w(TAG, "Method for data usage feedback isn't specified. Ignoring.");
788046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            successful = false;
788146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } else {
788246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            successful = updateDataUsageStat(dataIds, usageType, currentTimeMillis) > 0;
788346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
788446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
788546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        // Handle old API. This doesn't affect the result of this entire method.
788646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] questionMarks = new String[ids.length];
788746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        Arrays.fill(questionMarks, "?");
788846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String where = Data._ID + " IN (" + TextUtils.join(",", questionMarks) + ")";
78895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        final Cursor cursor = mActiveDb.get().query(
7890ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                Views.DATA,
789146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                new String[] { Data.CONTACT_ID },
789246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                where, ids, null, null, null);
789346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        try {
789446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            while (cursor.moveToNext()) {
789546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mSelectionArgs1[0] = cursor.getString(0);
789646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                ContentValues values2 = new ContentValues();
789746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                values2.put(Contacts.LAST_TIME_CONTACTED, currentTimeMillis);
78985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mActiveDb.get().update(Tables.CONTACTS, values2, Contacts._ID + "=?",
78995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mSelectionArgs1);
79005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mActiveDb.get().execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
79015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mActiveDb.get().execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
790246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
790346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } finally {
790446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            cursor.close();
790546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
790646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
790746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        return successful;
790846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
790946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
791046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
791146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Update {@link Tables#DATA_USAGE_STAT}.
791246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     *
791346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * @return the number of rows affected.
791446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
7915f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa    @VisibleForTesting
7916f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa    /* package */ int updateDataUsageStat(
7917f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa            List<Long> dataIds, String type, long currentTimeMillis) {
791846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final int typeInt = sDataUsageTypeMap.get(type);
791946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String where = DataUsageStatColumns.DATA_ID + " =? AND "
792046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                + DataUsageStatColumns.USAGE_TYPE_INT + " =?";
792146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] columns =
792246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                new String[] { DataUsageStatColumns._ID, DataUsageStatColumns.TIMES_USED };
792346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final ContentValues values = new ContentValues();
792446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        for (Long dataId : dataIds) {
792546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            final String[] args = new String[] { dataId.toString(), String.valueOf(typeInt) };
79265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().beginTransaction();
792746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            try {
79285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                final Cursor cursor = mActiveDb.get().query(Tables.DATA_USAGE_STAT, columns, where,
79295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        args, null, null, null);
793046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                try {
793146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (cursor.getCount() > 0) {
793246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        if (!cursor.moveToFirst()) {
793346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            Log.e(TAG,
793446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    "moveToFirst() failed while getAccount() returned non-zero.");
793546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        } else {
793646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.clear();
793746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.put(DataUsageStatColumns.TIMES_USED, cursor.getInt(1) + 1);
793846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.put(DataUsageStatColumns.LAST_TIME_USED, currentTimeMillis);
79395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            mActiveDb.get().update(Tables.DATA_USAGE_STAT, values,
794046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    DataUsageStatColumns._ID + " =?",
794146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    new String[] { cursor.getString(0) });
794246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        }
794346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    } else {
794446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.clear();
794546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.DATA_ID, dataId);
794646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.USAGE_TYPE_INT, typeInt);
794746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.TIMES_USED, 1);
794846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.LAST_TIME_USED, currentTimeMillis);
79495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mActiveDb.get().insert(Tables.DATA_USAGE_STAT, null, values);
795046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    }
79515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mActiveDb.get().setTransactionSuccessful();
795246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                } finally {
795346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    cursor.close();
795446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
795546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            } finally {
79565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mActiveDb.get().endTransaction();
795746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
795846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
795946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
796046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        return dataIds.size();
796146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
796246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
796346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
796446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Returns a sort order String for promoting data rows (email addresses, phone numbers, etc.)
796546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * associated with a primary account. The primary account should be supplied from applications
796646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * with {@link ContactsContract#PRIMARY_ACCOUNT_NAME} and
796746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * {@link ContactsContract#PRIMARY_ACCOUNT_TYPE}. Null will be returned when the primary
796846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * account isn't available.
796946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
797046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private String getAccountPromotionSortOrder(Uri uri) {
797146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String primaryAccountName =
797246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME);
797346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String primaryAccountType =
797446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE);
797546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
797646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        // Data rows associated with primary account should be promoted.
797746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (!TextUtils.isEmpty(primaryAccountName)) {
797846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            StringBuilder sb = new StringBuilder();
797946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            sb.append("(CASE WHEN " + RawContacts.ACCOUNT_NAME + "=");
798046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            DatabaseUtils.appendEscapedSQLString(sb, primaryAccountName);
798146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            if (!TextUtils.isEmpty(primaryAccountType)) {
798246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                sb.append(" AND " + RawContacts.ACCOUNT_TYPE + "=");
798346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DatabaseUtils.appendEscapedSQLString(sb, primaryAccountType);
798446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
798546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            sb.append(" THEN 0 ELSE 1 END)");
798646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            return sb.toString();
798746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } else {
798846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            return null;
798946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
799046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
7991b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
7992b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    /**
7993b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * Checks the URI for a deferred snippeting request
7994b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * @return a boolean indicating if a deferred snippeting request is in the RI
7995b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     */
7996b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private boolean deferredSnippetingRequested(Uri uri) {
7997b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        String deferredSnippeting =
7998b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            getQueryParameter(uri, SearchSnippetColumns.DEFERRED_SNIPPETING_KEY);
7999b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        return !TextUtils.isEmpty(deferredSnippeting) &&  deferredSnippeting.equals("1");
8000b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    }
8001b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
8002b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    /**
8003b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * Checks if query is a single word or not.
8004b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * @return a boolean indicating if the query is one word or not
8005b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     */
8006b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private boolean isSingleWordQuery(String query) {
8007b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        return query.split(QUERY_TOKENIZER_REGEX).length == 1;
8008b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    }
8009b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
8010b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    /**
8011b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * Checks the projection for a SNIPPET column indicating that a snippet is needed
8012b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * @return a boolean indicating if a snippet is needed or not.
8013b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     */
8014b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private boolean snippetNeeded(String [] projection) {
8015b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        return mDbHelper.get().isInProjection(projection, SearchSnippetColumns.SNIPPET);
8016b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    }
80174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
8018