ContactsProvider2.java revision 43368a3f9e05a979e454e278d6a0e8475f08923d
14f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/*
24f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Copyright (C) 2009 The Android Open Source Project
34f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton *
44f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Licensed under the Apache License, Version 2.0 (the "License");
54f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * you may not use this file except in compliance with the License.
64f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * You may obtain a copy of the License at
74f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton *
84f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton *      http://www.apache.org/licenses/LICENSE-2.0
94f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton *
104f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Unless required by applicable law or agreed to in writing, software
114f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * distributed under the License is distributed on an "AS IS" BASIS,
124f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * See the License for the specific language governing permissions and
144f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * limitations under the License
154f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1728f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarpackage com.android.providers.contacts;
1828f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar
19d9ec58265ae59a549880ef63cdfb5d0d977cdabaDmitri Plotnikovimport com.android.common.content.SQLiteContentProvider;
2053214b3ed12b0ff9cb589b6559311f2ac142f2e3Bjorn Bringertimport com.android.common.content.SyncStateContentProviderHelper;
215b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikovimport com.android.providers.contacts.ContactAggregator.AggregationSuggestionParameter;
2297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactLookupKey.LookupKeySegment;
2324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoroimport com.android.providers.contacts.ContactsDatabaseHelper.AccountsColumns;
2497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.AggregatedPresenceColumns;
2597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns;
2697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.Clauses;
2797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.ContactsColumns;
2897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.ContactsStatusUpdatesColumns;
2997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
3071340347b4862d4b1368a5d69d1667e2245952e4Daisuke Miyakawaimport com.android.providers.contacts.ContactsDatabaseHelper.DataUsageStatColumns;
3197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
322f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawaimport com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns;
3397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupColumns;
3497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
3597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneColumns;
3697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
371dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoroimport com.android.providers.contacts.ContactsDatabaseHelper.PhotoFilesColumns;
3897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
3997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
4003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.SearchIndexColumns;
4197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.SettingsColumns;
4297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.StatusUpdatesColumns;
433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmannimport com.android.providers.contacts.ContactsDatabaseHelper.StreamItemPhotosColumns;
44f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport com.android.providers.contacts.ContactsDatabaseHelper.StreamItemsColumns;
4597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.Tables;
46ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmannimport com.android.providers.contacts.ContactsDatabaseHelper.Views;
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;
74f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringertimport android.content.res.AssetFileDescriptor;
753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmannimport android.content.res.Resources;
76e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikovimport android.database.CrossProcessCursor;
774f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.Cursor;
78e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikovimport android.database.CursorWindow;
79ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.database.CursorWrapper;
80ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.database.DatabaseUtils;
8109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor;
8209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor.RowBuilder;
834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteDatabase;
8408ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwoodimport android.database.sqlite.SQLiteDoneException;
854f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteQueryBuilder;
86f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.graphics.Bitmap;
87f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.graphics.BitmapFactory;
884f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.net.Uri;
89d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.net.Uri.Builder;
90bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Binder;
916ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.os.Bundle;
92bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Handler;
93bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.HandlerThread;
94bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Message;
95ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringertimport android.os.ParcelFileDescriptor;
96bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Process;
97b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.os.RemoteException;
9815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikovimport android.os.StrictMode;
990dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikovimport android.os.SystemClock;
1000e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport android.os.SystemProperties;
1013d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.preference.PreferenceManager;
102508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkeyimport android.provider.BaseColumns;
1033de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract;
104b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions;
10597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Email;
10697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
10797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Im;
10897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Nickname;
1096d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Note;
11097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Organization;
11197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Phone;
11297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Photo;
1134928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.SipAddress;
11497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
11597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
116ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.provider.ContactsContract.ContactCounts;
1173de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Contacts;
1185b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikovimport android.provider.ContactsContract.Contacts.AggregationSuggestions;
1193de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Data;
12071340347b4862d4b1368a5d69d1667e2245952e4Daisuke Miyakawaimport android.provider.ContactsContract.DataUsageFeedback;
121d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.provider.ContactsContract.Directory;
122f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.provider.ContactsContract.DisplayPhoto;
1233de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Groups;
124bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.provider.ContactsContract.Intents;
1253de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.PhoneLookup;
1261dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoroimport android.provider.ContactsContract.PhotoFiles;
12709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.provider.ContactsContract.ProviderStatus;
1283de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.RawContacts;
129916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikovimport android.provider.ContactsContract.SearchSnippetColumns;
1303de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Settings;
13182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikovimport android.provider.ContactsContract.StatusUpdates;
1323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmannimport android.provider.ContactsContract.StreamItemPhotos;
133f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.provider.ContactsContract.StreamItems;
13497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.LiveFolders;
13597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.OpenableColumns;
13697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.SyncStateContract;
137a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.telephony.PhoneNumberUtils;
1389a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikovimport android.telephony.TelephonyManager;
139a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.text.TextUtils;
140c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.util.Log;
1414f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
142108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawaimport java.io.BufferedWriter;
143d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.ByteArrayOutputStream;
144f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport java.io.File;
145b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport java.io.FileNotFoundException;
146d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.IOException;
147d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.OutputStream;
148108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawaimport java.io.OutputStreamWriter;
149108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawaimport java.io.Writer;
15042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.text.SimpleDateFormat;
1517e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport java.util.ArrayList;
15246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawaimport java.util.Arrays;
1535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.Collections;
15442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.util.Date;
155b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport java.util.HashMap;
1560e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.HashSet;
1575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.List;
158622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkeyimport java.util.Locale;
159b5a4add17815167d20a90645779df34cdf45280dFred Quintanaimport java.util.Map;
1600e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.Set;
161ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikovimport java.util.concurrent.CountDownLatch;
1624f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1634f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/**
1644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Contacts content provider. The contract between this provider and applications
1654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * is defined in {@link ContactsContract}.
1664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
1675b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanapublic class ContactsProvider2 extends SQLiteContentProvider implements OnAccountsUpdateListener {
168caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
169bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final String TAG = "ContactsProvider";
170bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov
171bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE);
1724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
17315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_INITIALIZE = 0;
17415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_OPEN_WRITE_ACCESS = 1;
17515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS = 2;
17615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_ACCOUNTS = 3;
17715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_LOCALE = 4;
17815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM = 5;
17905e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_SEARCH_INDEX = 6;
18005e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_PROVIDER_STATUS = 7;
18105e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_DIRECTORIES = 8;
18205e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_CHANGE_LOCALE = 9;
183f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int BACKGROUND_TASK_CLEANUP_PHOTOS = 10;
184619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Default for the maximum number of returned aggregation suggestions. */
1863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DEFAULT_MAX_SUGGESTIONS = 5;
1873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /** Limit for the maximum number of social stream items to store under a raw contact. */
1893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int MAX_STREAM_ITEMS_PER_RAW_CONTACT = 5;
1903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
191f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /** Rate limit (in ms) for photo cleanup.  Do it at most once per day. */
192f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int PHOTO_CLEANUP_RATE_LIMIT = 24 * 60 * 60 * 1000;
193f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1943d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /**
195b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov     * Property key for the legacy contact import version. The need for a version
1963d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * as opposed to a boolean flag is that if we discover bugs in the contact import process,
1973d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * we can trigger re-import by incrementing the import version.
1983d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     */
199b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final String PROPERTY_CONTACTS_IMPORTED = "contacts_imported_v1";
200b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final int PROPERTY_CONTACTS_IMPORT_VERSION = 1;
20151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    private static final String PREF_LOCALE = "locale";
2023d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2030dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static final String PROPERTY_AGGREGATION_ALGORITHM = "aggregation_v2";
2040dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static final int PROPERTY_AGGREGATION_ALGORITHM_VERSION = 2;
2050dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
2060e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff    private static final String AGGREGATE_CONTACTS = "sync.contacts.aggregate";
2070e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff
208a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
2094f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2102f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    /**
2112f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     * Used to insert a column into strequent results, which enables SQL to sort the list using
2122f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     * the total times contacted. See also {@link #sStrequentFrequentProjectionMap}.
2132f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     */
2142f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    private static final String TIMES_USED_SORT_COLUMN = "times_used_sort";
2155e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
216d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final String STREQUENT_ORDER_BY = Contacts.STARRED + " DESC, "
2172f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            + TIMES_USED_SORT_COLUMN + " DESC, "
2189b43551f1ce33b79141772737a262ce609bd0cebMegha Joshi            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
219d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final String STREQUENT_LIMIT =
220d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            "(SELECT COUNT(1) FROM " + Tables.CONTACTS + " WHERE "
221d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            + Contacts.STARRED + "=1) + 25";
222d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
22345ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa    private static final String FREQUENT_ORDER_BY = DataUsageStatColumns.TIMES_USED + " DESC,"
22445ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
22545ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa
2266e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_CONTACTS_TABLE =
2279b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.CONTACTS + " SET " + Contacts.TIMES_CONTACTED + "=" +
2289b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + Contacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
2299b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + Contacts.TIMES_CONTACTED + " + 1) END WHERE " + Contacts._ID + "=?";
2309b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
2316e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE =
2329b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.RAW_CONTACTS + " SET " + RawContacts.TIMES_CONTACTED + "=" +
2339b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + RawContacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
2349b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + RawContacts.TIMES_CONTACTED + " + 1) END WHERE " + RawContacts.CONTACT_ID + "=?";
2359b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
236de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa    /* package */ static final String PHONEBOOK_COLLATOR_NAME = "PHONEBOOK";
237de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa
2383716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    // Regex for splitting query strings - we split on any group of non-alphanumeric characters,
2393716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    // excluding the @ symbol.
2403716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    /* package */ static final String QUERY_TOKENIZER_REGEX = "[^\\w@]+";
2413716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
242d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS = 1000;
243d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS_ID = 1001;
2445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP = 1002;
2455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID = 1003;
246a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_DATA = 1004;
2475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_FILTER = 1005;
2485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT = 1006;
2495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT_FILTER = 1007;
2505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_GROUP = 1008;
251a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_PHOTO = 1009;
252f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_ID_DISPLAY_PHOTO = 1010;
253f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_LOOKUP_DISPLAY_PHOTO = 1011;
254f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_LOOKUP_ID_DISPLAY_PHOTO = 1012;
255f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_AS_VCARD = 1013;
256f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_AS_MULTI_VCARD = 1014;
257f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_LOOKUP_DATA = 1015;
258f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_LOOKUP_ID_DATA = 1016;
259f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_ID_ENTITIES = 1017;
260f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_LOOKUP_ENTITIES = 1018;
261f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_LOOKUP_ID_ENTITIES = 1019;
262f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_ID_STREAM_ITEMS = 1020;
263f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_LOOKUP_STREAM_ITEMS = 1021;
264f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_LOOKUP_ID_STREAM_ITEMS = 1022;
26545ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa    private static final int CONTACTS_FREQUENT = 1023;
2664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2675ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS = 2002;
2685ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_ID = 2003;
2695ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_DATA = 2004;
27046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITY_ID = 2005;
271f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int RAW_CONTACTS_ID_DISPLAY_PHOTO = 2006;
272f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int RAW_CONTACTS_ID_STREAM_ITEMS = 2007;
2734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2746bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
2756bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
276ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
27748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_ID = 3003;
27848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_FILTER = 3004;
27948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS = 3005;
28048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_ID = 3006;
28148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_LOOKUP = 3007;
28248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_FILTER = 3008;
28348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS = 3009;
28448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS_ID = 3010;
285a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2866bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
2876bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
288b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
289b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
290b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
29182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES = 7000;
29282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES_ID = 7001;
2931f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
29431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
29531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
296eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final int SETTINGS = 9000;
297eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
298ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
299ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
300ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
301ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
30235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
303b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private static final int SYNCSTATE_ID = 11001;
30435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
305c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SUGGESTIONS = 12001;
306c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SHORTCUT = 12002;
307c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
3081b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS = 14000;
3091b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_WITH_PHONES = 14001;
3101b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_FAVORITES = 14002;
3111b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_GROUP_NAME = 14003;
3121b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
31346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITIES = 15001;
31446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
31509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private static final int PROVIDER_STATUS = 16001;
31609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
317d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES = 17001;
318d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES_ID = 17002;
319d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3207a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private static final int COMPLETE_NAME = 18000;
3217a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
32224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE = 19000;
32324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_ENTITIES = 19001;
32424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_DATA = 19002;
32524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_DATA_ID = 19003;
32624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_AS_VCARD = 19004;
32724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS = 19005;
32824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID = 19006;
32924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID_DATA = 19007;
33024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID_ENTITIES = 19008;
33124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
33246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final int DATA_USAGE_FEEDBACK_ID = 20001;
33346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
3343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS = 21000;
3353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_PHOTOS = 21001;
3363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID = 21002;
3373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID_PHOTOS = 21003;
3383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID_PHOTOS_ID = 21004;
3393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_LIMIT = 21005;
3403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
341f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int DISPLAY_PHOTO = 22000;
342f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int PHOTO_DIMENSIONS = 22001;
343f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
344dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID =
345dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
346dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME
347dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
348dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE
34943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AND ("
35043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + GroupsColumns.CONCRETE_DATA_SET
35143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + "=" + RawContactsColumns.CONCRETE_DATA_SET + " OR "
35243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + GroupsColumns.CONCRETE_DATA_SET + " IS NULL AND "
35343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + RawContactsColumns.CONCRETE_DATA_SET + " IS NULL)"
354dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " AND " + Groups.FAVORITES + " != 0";
355dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
356dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID =
357dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
358dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
359dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
360dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
36143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AND ("
36243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + GroupsColumns.CONCRETE_DATA_SET + "="
36343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + RawContactsColumns.CONCRETE_DATA_SET + " OR "
36443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + GroupsColumns.CONCRETE_DATA_SET + " IS NULL AND "
36543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + RawContactsColumns.CONCRETE_DATA_SET + " IS NULL)"
36643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + " AND " + Groups.AUTO_ADD + " != 0";
367dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
368dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String[] PROJECTION_GROUP_ID
369dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            = new String[]{Tables.GROUPS + "." + Groups._ID};
370dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
371dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_GROUPMEMBERSHIP_DATA = DataColumns.MIMETYPE_ID + "=? "
372dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.GROUP_ROW_ID + "=? "
373dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.RAW_CONTACT_ID + "=?";
374dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
375dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_STARRED_FROM_RAW_CONTACTS =
376dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            "SELECT " + RawContacts.STARRED
377dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " FROM " + Tables.RAW_CONTACTS + " WHERE " + RawContacts._ID + "=?";
378dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
379e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    public class AddressBookCursor extends CursorWrapper implements CrossProcessCursor {
380e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        private final CrossProcessCursor mCursor;
381e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        private final Bundle mBundle;
382e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
383e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public AddressBookCursor(CrossProcessCursor cursor, String[] titles, int[] counts) {
384e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            super(cursor);
385e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mCursor = cursor;
386e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mBundle = new Bundle();
387e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mBundle.putStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES, titles);
388e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mBundle.putIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS, counts);
389e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
390e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
391e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        @Override
392e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public Bundle getExtras() {
393e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return mBundle;
394e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
395e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
396e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        @Override
397e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public void fillWindow(int pos, CursorWindow window) {
398e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mCursor.fillWindow(pos, window);
399e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
400e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
401e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        @Override
402e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public CursorWindow getWindow() {
403e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return mCursor.getWindow();
404e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
405e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
406e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        @Override
407e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public boolean onMove(int oldPosition, int newPosition) {
408e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return mCursor.onMove(oldPosition, newPosition);
409e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
410e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    }
411e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
412d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private interface DataContactsQuery {
413f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        public static final String TABLE = "data "
414f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) "
415f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN contacts ON (raw_contacts.contact_id = contacts._id)";
41667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
41767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
4186cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
4196802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            RawContactsColumns.CONCRETE_ACCOUNT_TYPE,
4206802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            RawContactsColumns.CONCRETE_ACCOUNT_NAME,
42143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            RawContactsColumns.CONCRETE_DATA_SET,
4223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
423f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            ContactsColumns.CONCRETE_ID
424ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
425ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
426d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
4276802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        public static final int ACCOUNT_TYPE = 1;
4286802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        public static final int ACCOUNT_NAME = 2;
42943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        public static final int DATA_SET = 3;
43043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        public static final int DATA_ID = 4;
43143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        public static final int CONTACT_ID = 5;
432ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
4331f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
434f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov    interface RawContactsQuery {
43519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String TABLE = Tables.RAW_CONTACTS;
43619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
43719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String[] COLUMNS = new String[] {
438ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.DELETED,
439ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
440ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
44143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.DATA_SET,
44243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
44319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        };
44419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
44519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int DELETED = 0;
446ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_TYPE = 1;
447ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_NAME = 2;
44843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int DATA_SET = 3;
44943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int ACCOUNT_TYPE_AND_DATA_SET = 4;
45019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    }
45119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
452c76cdd0723b99f478c9ba5329d14a971cd8dfb3dCostin Manolache    public static final String DEFAULT_ACCOUNT_TYPE = "com.google";
453caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
45471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    /** Sql where statement for filtering on groups. */
45571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private static final String CONTACTS_IN_GROUP_SELECT =
45671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov            Contacts._ID + " IN "
45771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + "(SELECT " + RawContacts.CONTACT_ID
45871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " FROM " + Tables.RAW_CONTACTS
45971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + " IN "
46071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
46171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " FROM " + Tables.DATA_JOIN_MIMETYPES
46271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " WHERE " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
46371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "' AND " + GroupMembership.GROUP_ROW_ID + "="
46471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "(SELECT " + Tables.GROUPS + "." + Groups._ID
46571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " FROM " + Tables.GROUPS
46671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " WHERE " + Groups.TITLE + "=?)))";
46771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov
468a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating DIRTY flag on multiple raw contacts */
469a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_DIRTY_SQL =
470a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
471a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.DIRTY + "=1" +
472a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
473a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
474a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating VERSION on multiple raw contacts */
475a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_VERSION_SQL =
476a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
477a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.VERSION + " = " + RawContacts.VERSION + " + 1" +
478a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
479a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
480c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Current contacts - those contacted within the last 3 days (in seconds)
481c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_CURRENT = 3 * 24 * 60 * 60;
482c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
483c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Recent contacts - those contacted within the last 30 days (in seconds)
484c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_RECENT = 30 * 24 * 60 * 60;
485c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
486f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa    private static final String TIME_SINCE_LAST_USED =
487f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa            "(strftime('%s', 'now') - " + DataUsageStatColumns.LAST_TIME_USED + "/1000)";
488f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa
489c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    /*
490c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * Sorting order for email address suggestions: first starred, then the rest.
4912262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * second in_visible_group, then the rest.
4922262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * Within the four (starred/unstarred, in_visible_group/not-in_visible_group) groups
4932262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * - three buckets: very recently contacted, then fairly
494c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * recently contacted, then the rest.  Within each of the bucket - descending count
49546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * of times contacted (both for data row and for contact row). If all else fails, alphabetical.
49646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * (Super)primary email address is returned before other addresses for the same contact.
497c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     */
498c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final String EMAIL_FILTER_SORT_ORDER =
4992262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa        Contacts.STARRED + " DESC, "
5002262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa        + Contacts.IN_VISIBLE_GROUP + " DESC, "
501f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa        + "(CASE WHEN " + TIME_SINCE_LAST_USED + " < " + EMAIL_FILTER_CURRENT
50246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " THEN 0 "
503f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa                + " WHEN " + TIME_SINCE_LAST_USED + " < " + EMAIL_FILTER_RECENT
50446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " THEN 1 "
50546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " ELSE 2 END), "
50646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + DataUsageStatColumns.TIMES_USED + " DESC, "
50746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + Contacts.DISPLAY_NAME + ", "
50846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + Data.CONTACT_ID + ", "
509c591cc2ffecdd0038f787a133606752752294c13Daisuke Miyakawa        + Data.IS_SUPER_PRIMARY + " DESC, "
510c591cc2ffecdd0038f787a133606752752294c13Daisuke Miyakawa        + Data.IS_PRIMARY + " DESC";
51146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
51246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /** Currently same as {@link #EMAIL_FILTER_SORT_ORDER} */
51346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final String PHONE_FILTER_SORT_ORDER = EMAIL_FILTER_SORT_ORDER;
514c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
515916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Name lookup types used for contact filtering */
516916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private static final String CONTACT_LOOKUP_NAME_TYPES =
517916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NAME_COLLATION_KEY + "," +
518916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.EMAIL_BASED_NICKNAME + "," +
51992ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov            NameLookupType.NICKNAME;
520916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
521f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    /**
522f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * If any of these columns are used in a Data projection, there is no point in
523f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * using the DISTINCT keyword, which can negatively affect performance.
524f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     */
525f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    private static final String[] DISTINCT_DATA_PROHIBITING_COLUMNS = {
526f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data._ID,
527f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.RAW_CONTACT_ID,
528f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.NAME_RAW_CONTACT_ID,
529f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_NAME,
530f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_TYPE,
53143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            RawContacts.DATA_SET,
53243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
533f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.DIRTY,
534f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.NAME_VERIFIED,
535f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.SOURCE_ID,
536f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.VERSION,
537f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    };
538916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
539f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsColumns = ProjectionMap.builder()
540f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CUSTOM_RINGTONE)
541f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME)
542f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_ALTERNATIVE)
543f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_SOURCE)
544f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.IN_VISIBLE_GROUP)
545f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LAST_TIME_CONTACTED)
546f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LOOKUP_KEY)
547f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME)
548f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME_STYLE)
549f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHOTO_ID)
550f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            .add(Contacts.PHOTO_FILE_ID)
5513d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_URI)
5523d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_THUMBNAIL_URI)
553f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SEND_TO_VOICEMAIL)
554f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_ALTERNATIVE)
555f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_PRIMARY)
556f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.STARRED)
557f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.TIMES_CONTACTED)
558cf832869bcf91b8037d8b7f510a3a213b30764a3Dmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
559f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
560f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
561f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsPresenceColumns = ProjectionMap.builder()
562f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
563f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE)
564f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
565f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
566f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
567f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
568f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
569f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
570f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
571f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
572f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
573f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
574f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
575f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
576f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
577f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
578f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSnippetColumns = ProjectionMap.builder()
57903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET)
580f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
581f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
582f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactColumns = ProjectionMap.builder()
583f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_NAME)
584f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_TYPE)
58543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(RawContacts.DATA_SET)
58643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(RawContacts.ACCOUNT_TYPE_AND_DATA_SET)
587f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DIRTY)
588f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.NAME_VERIFIED)
589f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SOURCE_ID)
590f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.VERSION)
591f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
592f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
593f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactSyncColumns = ProjectionMap.builder()
594f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC1)
595f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC2)
596f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC3)
597f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC4)
598f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
599f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
600f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataColumns = ProjectionMap.builder()
601f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA1)
602f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA2)
603f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA3)
604f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA4)
605f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA5)
606f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA6)
607f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA7)
608f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA8)
609f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA9)
610f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA10)
611f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA11)
612f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA12)
613f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA13)
614f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA14)
615f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA15)
616f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA_VERSION)
617f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_PRIMARY)
618f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_SUPER_PRIMARY)
619f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.MIMETYPE)
620f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RES_PACKAGE)
621f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC1)
622f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC2)
623f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC3)
624f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC4)
625f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(GroupMembership.GROUP_SOURCE_ID)
626f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
627f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
628f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactPresenceColumns = ProjectionMap.builder()
629f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
630f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.PRESENCE)
631f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
632f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.CHAT_CAPABILITY)
633f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
634f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
635f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
636f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
637f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
638f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
639f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
640f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
641f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
642f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
643f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
644f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
645f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataPresenceColumns = ProjectionMap.builder()
646f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.PRESENCE, Tables.PRESENCE + "." + StatusUpdates.PRESENCE)
647f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CHAT_CAPABILITY, Tables.PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
648f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS, StatusUpdatesColumns.CONCRETE_STATUS)
649f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_TIMESTAMP, StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
650f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_RES_PACKAGE, StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
651f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_LABEL, StatusUpdatesColumns.CONCRETE_STATUS_LABEL)
652f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_ICON, StatusUpdatesColumns.CONCRETE_STATUS_ICON)
653f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
654f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
655038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    /** Contains just BaseColumns._COUNT */
656f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sCountProjectionMap = ProjectionMap.builder()
657f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(BaseColumns._COUNT, "COUNT(*)")
658f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
659f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
660e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    /** Contains just the contacts columns */
661f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionMap = ProjectionMap.builder()
662f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts._ID)
663f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
664f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.NAME_RAW_CONTACT_ID)
66524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(Contacts.IS_USER_PROFILE)
666f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
667f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsPresenceColumns)
668f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
669f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
670916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Contains just the contacts columns */
671f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionWithSnippetMap = ProjectionMap.builder()
672f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
673f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sSnippetColumns)
674f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
675916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
6765e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    /** Used for pushing starred contacts to the top of a times contacted list **/
677f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentStarredProjectionMap = ProjectionMap.builder()
678f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
6792f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, String.valueOf(Long.MAX_VALUE))
680f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
681f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
682f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentFrequentProjectionMap = ProjectionMap.builder()
683f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
6842f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, "SUM(" + DataUsageStatColumns.CONCRETE_TIMES_USED + ")")
685f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
686f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6874928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    /**
6884928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * Used for Strequent Uri with {@link ContactsContract#STREQUENT_PHONE_ONLY}, which allows
6894928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * users to obtain part of Data columns. Right now Starred part just returns NULL for
6904928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * those data columns (frequent part should return real ones in data table).
6914928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     **/
6924928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    private static final ProjectionMap sStrequentPhoneOnlyStarredProjectionMap
6934928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            = ProjectionMap.builder()
6944928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .addAll(sContactsProjectionMap)
6954928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, String.valueOf(Long.MAX_VALUE))
6964928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.NUMBER, "NULL")
6974928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.TYPE, "NULL")
6984928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.LABEL, "NULL")
6994928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .build();
7004928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
7014928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    /**
7024928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * Used for Strequent Uri with {@link ContactsContract#STREQUENT_PHONE_ONLY}, which allows
7034928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * users to obtain part of Data columns. We hard-code {@link Contacts#IS_USER_PROFILE} to NULL,
7044928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * because sContactsProjectionMap specifies a field that doesn't exist in the view behind the
7054928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * query that uses this projection map.
7064928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     **/
7074928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    private static final ProjectionMap sStrequentPhoneOnlyFrequentProjectionMap
7084928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            = ProjectionMap.builder()
7094928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .addAll(sContactsProjectionMap)
7104928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, DataUsageStatColumns.CONCRETE_TIMES_USED)
7114928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.NUMBER)
7124928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.TYPE)
7134928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.LABEL)
7144928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Contacts.IS_USER_PROFILE, "NULL")
7154928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .build();
7164928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
717f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    /** Contains just the contacts vCard columns */
718f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsVCardProjectionMap = ProjectionMap.builder()
719fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            .add(Contacts._ID)
720f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.DISPLAY_NAME, Contacts.DISPLAY_NAME + " || '.vcf'")
721f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.SIZE, "NULL")
722f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
723f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
724ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    /** Contains just the raw contacts columns */
725f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactsProjectionMap = ProjectionMap.builder()
726f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
727f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
728f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
729f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_PRIMARY)
730f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_ALTERNATIVE)
731f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_SOURCE)
732f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME)
733f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME_STYLE)
734f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_PRIMARY)
735f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_ALTERNATIVE)
736f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.TIMES_CONTACTED)
737f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.LAST_TIME_CONTACTED)
738f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CUSTOM_RINGTONE)
739f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SEND_TO_VOICEMAIL)
740f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
741f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.AGGREGATION_MODE)
74224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
743f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
744f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
745f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
746f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
747a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the raw entity view*/
748f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawEntityProjectionMap = ProjectionMap.builder()
749f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
750f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
751f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.Entity.DATA_ID)
752f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
753f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
75424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
755f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
756f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
757f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
758f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
759f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
760a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the contact entity view*/
761f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sEntityProjectionMap = ProjectionMap.builder()
762f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity._ID)
763f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.CONTACT_ID)
764f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.RAW_CONTACT_ID)
765f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DATA_ID)
766f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.NAME_RAW_CONTACT_ID)
767f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DELETED)
76824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(Contacts.IS_USER_PROFILE)
769f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
770f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
771f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
772f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
773f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
774f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
775f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
776f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
7774a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /** Contains columns from the data view */
778f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataProjectionMap = ProjectionMap.builder()
779f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID)
780f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RAW_CONTACT_ID)
781f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CONTACT_ID)
782f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.NAME_RAW_CONTACT_ID)
78324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
784f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
785f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
786f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
787f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
788f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
789f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
790f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
7915e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /** Contains columns from the data view */
792f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDistinctDataProjectionMap = ProjectionMap.builder()
793f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID, "MIN(" + Data._ID + ")")
794f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
79524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
796f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
797f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
798f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
799f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
800f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
801f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
8029261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
803f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sPhoneLookupProjectionMap = ProjectionMap.builder()
804f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup._ID, "contacts_view." + Contacts._ID)
805f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LOOKUP_KEY, "contacts_view." + Contacts.LOOKUP_KEY)
806f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.DISPLAY_NAME, "contacts_view." + Contacts.DISPLAY_NAME)
807f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LAST_TIME_CONTACTED, "contacts_view." + Contacts.LAST_TIME_CONTACTED)
808f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TIMES_CONTACTED, "contacts_view." + Contacts.TIMES_CONTACTED)
809f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.STARRED, "contacts_view." + Contacts.STARRED)
810f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.IN_VISIBLE_GROUP, "contacts_view." + Contacts.IN_VISIBLE_GROUP)
811f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.PHOTO_ID, "contacts_view." + Contacts.PHOTO_ID)
8123d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_URI, "contacts_view." + Contacts.PHOTO_URI)
8133d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_THUMBNAIL_URI, "contacts_view." + Contacts.PHOTO_THUMBNAIL_URI)
814f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.CUSTOM_RINGTONE, "contacts_view." + Contacts.CUSTOM_RINGTONE)
815f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.HAS_PHONE_NUMBER, "contacts_view." + Contacts.HAS_PHONE_NUMBER)
816f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.SEND_TO_VOICEMAIL, "contacts_view." + Contacts.SEND_TO_VOICEMAIL)
817f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.NUMBER, Phone.NUMBER)
818f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TYPE, Phone.TYPE)
819f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LABEL, Phone.LABEL)
8202530512f639c4979fd7371c7dd25dd67e8118124Bai Tao            .add(PhoneLookup.NORMALIZED_NUMBER, Phone.NORMALIZED_NUMBER)
821f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
822f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
823ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
824f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsProjectionMap = ProjectionMap.builder()
825f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups._ID)
826f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_NAME)
827f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_TYPE)
82843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(Groups.DATA_SET)
82943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(Groups.ACCOUNT_TYPE_AND_DATA_SET)
830f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SOURCE_ID)
831f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DIRTY)
832f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.VERSION)
833f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.RES_PACKAGE)
834f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE)
835f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE_RES)
836f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.GROUP_VISIBLE)
837f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYSTEM_ID)
838f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DELETED)
839f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.NOTES)
8401cdfc9dacc136e99d3c0bc5b4212bc3c973be337Daniel Lehmann            .add(Groups.ACTION)
8411cdfc9dacc136e99d3c0bc5b4212bc3c973be337Daniel Lehmann            .add(Groups.ACTION_URI)
842f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SHOULD_SYNC)
843f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.FAVORITES)
844f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.AUTO_ADD)
845c039cfb78c40730483fd71178df63ada5826a315Dmitri Plotnikov            .add(Groups.GROUP_IS_READ_ONLY)
846f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC1)
847f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC2)
848f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC3)
849f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC4)
850f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
851f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
852ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains {@link Groups} columns along with summary details */
853f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsSummaryProjectionMap = ProjectionMap.builder()
854f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sGroupsProjectionMap)
855f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_COUNT,
856f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                    "(SELECT COUNT(" + ContactsColumns.CONCRETE_ID + ") FROM "
857f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + Tables.CONTACTS_JOIN_RAW_CONTACTS_DATA_FILTERED_BY_GROUPMEMBERSHIP
858f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + ")")
859f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_WITH_PHONES,
860f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                    "(SELECT COUNT(" + ContactsColumns.CONCRETE_ID + ") FROM "
861f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + Tables.CONTACTS_JOIN_RAW_CONTACTS_DATA_FILTERED_BY_GROUPMEMBERSHIP
862f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + " WHERE " + Contacts.HAS_PHONE_NUMBER + ")")
863f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            .build();
864f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa
865f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa    // This is only exposed as hidden API for the contacts app, so we can be very specific in
866f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa    // the filtering
867f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa    private static final ProjectionMap sGroupsSummaryProjectionMapWithGroupCountPerAccount =
868f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            ProjectionMap.builder()
869f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            .addAll(sGroupsSummaryProjectionMap)
870f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            .add(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT,
871f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                    "(SELECT COUNT(*) FROM " + Views.GROUPS + " WHERE "
872f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + "(" + Groups.ACCOUNT_NAME + "="
873f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + GroupsColumns.CONCRETE_ACCOUNT_NAME
874f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + " AND "
875f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.ACCOUNT_TYPE + "=" + GroupsColumns.CONCRETE_ACCOUNT_TYPE
876f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + " AND "
877f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.DELETED + "=0 AND "
878f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.FAVORITES + "=0 AND "
879f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.AUTO_ADD + "=0"
880f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + ")"
881f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + " GROUP BY "
882f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.ACCOUNT_NAME + ", " + Groups.ACCOUNT_TYPE
883f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                   + ")")
884f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
885f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
886373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains the agg_exceptions columns */
887f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sAggregationExceptionsProjectionMap = ProjectionMap.builder()
888f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id")
889f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.TYPE)
890f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID1)
891f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID2)
892f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
893f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
894eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    /** Contains the agg_exceptions columns */
895f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSettingsProjectionMap = ProjectionMap.builder()
896f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_NAME)
897f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_TYPE)
898f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_VISIBLE)
899f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.SHOULD_SYNC)
900f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ANY_UNSYNCED,
901f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN MIN(" + Settings.SHOULD_SYNC
902f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                        + ",(SELECT "
903f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + "(CASE WHEN MIN(" + Groups.SHOULD_SYNC + ") IS NULL"
904f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " THEN 1"
905f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " ELSE MIN(" + Groups.SHOULD_SYNC + ")"
906f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " END)"
907f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.GROUPS
908f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
909f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_NAME
910f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
911f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_TYPE + "))=0"
912f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN 1"
913f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE 0"
914f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " END)")
915f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_COUNT,
916f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
917f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
918f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
919f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
920f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
921f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
922f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_WITH_PHONES,
923f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
924f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
925f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
926f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + Contacts.HAS_PHONE_NUMBER
927f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
928f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
929f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
930f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
931f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
93282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Contains StatusUpdates columns */
933f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStatusUpdatesProjectionMap = ProjectionMap.builder()
934f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PresenceColumns.RAW_CONTACT_ID)
935f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.DATA_ID, DataColumns.CONCRETE_ID)
936f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_ACCOUNT)
937f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_HANDLE)
938f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PROTOCOL)
939f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // We cannot allow a null in the custom protocol field, because SQLite3 does not
940f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // properly enforce uniqueness of null values
941f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CUSTOM_PROTOCOL,
942f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN " + StatusUpdates.CUSTOM_PROTOCOL + "=''"
943f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN NULL"
944f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE " + StatusUpdates.CUSTOM_PROTOCOL + " END)")
945f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PRESENCE)
946f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CHAT_CAPABILITY)
947f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS)
948f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_TIMESTAMP)
949f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_RES_PACKAGE)
950f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_ICON)
951f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_LABEL)
952f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
953f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
9543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /** Contains StreamItems columns */
9553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final ProjectionMap sStreamItemsProjectionMap = ProjectionMap.builder()
9563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems._ID, StreamItemsColumns.CONCRETE_ID)
9573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(RawContacts.CONTACT_ID)
9583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RAW_CONTACT_ID)
9593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_PACKAGE)
9603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_ICON)
9613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_LABEL)
9623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.TEXT)
9633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.TIMESTAMP)
9643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.COMMENTS)
9653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.ACTION)
9663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.ACTION_URI)
9673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .build();
9683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
9693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final ProjectionMap sStreamItemPhotosProjectionMap = ProjectionMap.builder()
9703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos._ID, StreamItemPhotosColumns.CONCRETE_ID)
9713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RAW_CONTACT_ID)
9723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.STREAM_ITEM_ID)
9733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.SORT_INDEX)
9746802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            .add(StreamItemPhotos.PHOTO_FILE_ID)
9756802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            .add(StreamItemPhotos.PHOTO_URI,
9766802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    "'" + DisplayPhoto.CONTENT_URI + "'||'/'||" + StreamItemPhotos.PHOTO_FILE_ID)
9773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.ACTION, StreamItemPhotosColumns.CONCRETE_ACTION)
9783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.ACTION_URI, StreamItemPhotosColumns.CONCRETE_ACTION_URI)
9791dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.HEIGHT)
9801dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.WIDTH)
9811dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.FILESIZE)
9823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .build();
9833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
9841b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    /** Contains Live Folders columns */
985f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sLiveFoldersProjectionMap = ProjectionMap.builder()
986f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders._ID, Contacts._ID)
987f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders.NAME, Contacts.DISPLAY_NAME)
988f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // TODO: Put contact photo back when we have a way to display a default icon
989f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // for contacts without a photo
990f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // .add(LiveFolders.ICON_BITMAP, Photos.DATA)
991f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
992f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
993d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /** Contains {@link Directory} columns */
994f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDirectoryProjectionMap = ProjectionMap.builder()
995f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory._ID)
996f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.PACKAGE_NAME)
997f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.TYPE_RESOURCE_ID)
998f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DISPLAY_NAME)
999f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DIRECTORY_AUTHORITY)
1000f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_TYPE)
1001f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_NAME)
1002f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.EXPORT_SUPPORT)
1003778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.SHORTCUT_SUPPORT)
1004778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.PHOTO_SUPPORT)
1005f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
10067e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
10079705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    // where clause to update the status_updates table
10089705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private static final String WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE =
10099705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdatesColumns.DATA_ID + " IN (SELECT Distinct " + StatusUpdates.DATA_ID +
10109705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " FROM " + Tables.STATUS_UPDATES + " LEFT OUTER JOIN " + Tables.PRESENCE +
10119705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " ON " + StatusUpdatesColumns.DATA_ID + " = " + StatusUpdates.DATA_ID + " WHERE ";
10129705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
10132526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private static final String[] EMPTY_STRING_ARRAY = new String[0];
10142526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
1015bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1016bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Notification ID for failure to import contacts.
1017bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1018bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private static final int LEGACY_IMPORT_FAILED_NOTIFICATION = 1;
101951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
102003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_START_MATCH = "[";
102103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_END_MATCH = "]";
102203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_ELLIPSIS = "...";
102303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final int DEFAULT_SNIPPET_ARG_MAX_TOKENS = -10;
102403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
10259a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    private boolean sIsPhoneInitialized;
10269a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    private boolean sIsPhone;
10279a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
1028f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private StringBuilder mSb = new StringBuilder();
10291129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs1 = new String[1];
10301129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs2 = new String[2];
10312526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private ArrayList<String> mSelectionArgs = Lists.newArrayList();
10322526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
1033f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private Account mAccount;
1034f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
103546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
103646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Stores mapping from type Strings exposed via {@link DataUsageFeedback} to
103746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * type integers in {@link DataUsageStatColumns}.
103846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
103946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final Map<String, Integer> sDataUsageTypeMap;
104046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
10414f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
10424f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
1043a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
1044d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
1045d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
1046a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_ID_DATA);
1047a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/entities", CONTACTS_ID_ENTITIES);
10483653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
10493653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
10502d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*",
10512d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                AGGREGATION_SUGGESTIONS);
1052a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_ID_PHOTO);
1053f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/display_photo",
1054f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_ID_DISPLAY_PHOTO);
10553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/stream_items",
10563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_ID_STREAM_ITEMS);
1057c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter", CONTACTS_FILTER);
10585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER);
10595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP);
10602149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/data", CONTACTS_LOOKUP_DATA);
10615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
10622149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/data",
10632149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                CONTACTS_LOOKUP_ID_DATA);
1064f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/display_photo",
1065f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_LOOKUP_DISPLAY_PHOTO);
1066f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/display_photo",
1067f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_LOOKUP_ID_DISPLAY_PHOTO);
1068a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/entities",
1069a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ENTITIES);
1070a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/entities",
1071a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ID_ENTITIES);
10723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/stream_items",
10733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_LOOKUP_STREAM_ITEMS);
10743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/stream_items",
10753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_LOOKUP_ID_STREAM_ITEMS);
1076f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_vcard/*", CONTACTS_AS_VCARD);
107742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_multi_vcard/*",
107842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                CONTACTS_AS_MULTI_VCARD);
10795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT);
1080ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*",
1081ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                CONTACTS_STREQUENT_FILTER);
10825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP);
108345ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa        matcher.addURI(ContactsContract.AUTHORITY, "contacts/frequent", CONTACTS_FREQUENT);
10843653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
10855ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
10865ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
10875ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA);
1088f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/display_photo",
1089f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                RAW_CONTACTS_ID_DISPLAY_PHOTO);
109046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ENTITY_ID);
10913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/stream_items",
10923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                RAW_CONTACTS_ID_STREAM_ITEMS);
109346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
109446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contact_entities", RAW_CONTACT_ENTITIES);
1095b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
10964f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
10974f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
1098ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
109948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID);
11005e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER);
1101ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
11024a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS);
110348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID);
11041dac83b8fa58944acfd00f44e717a7dddc659d2dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup", EMAILS_LOOKUP);
11055e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP);
11065e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER);
11074a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
1108ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
110948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
111046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        /** "*" is in CSV form with data ids ("123,456,789") */
111146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        matcher.addURI(ContactsContract.AUTHORITY, "data/usagefeedback/*", DATA_USAGE_FEEDBACK_ID);
11121f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1113ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
1114ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
1115ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
1116ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
111735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
1118b5a4add17815167d20a90645779df34cdf45280dFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#",
1119b5a4add17815167d20a90645779df34cdf45280dFred Quintana                SYNCSTATE_ID);
112035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1121a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
1122b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
1123b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
1124b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
1125b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
11264f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1127eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS);
1128eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
112982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates", STATUS_UPDATES);
113082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates/#", STATUS_UPDATES_ID);
11311f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1132c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
1133c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
1134c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
1135c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
11362d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*",
1137c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SHORTCUT);
1138c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
11391b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts",
11401b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS);
11411b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts/*",
11421b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_GROUP_NAME);
11431b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts_with_phones",
11441b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_WITH_PHONES);
11451b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/favorites",
11461b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_FAVORITES);
114709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
114809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "provider_status", PROVIDER_STATUS);
1149d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
1150d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories", DIRECTORIES);
1151d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories/#", DIRECTORIES_ID);
11527a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
11537a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "complete_name", COMPLETE_NAME);
115424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
115524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile", PROFILE);
115624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/entities", PROFILE_ENTITIES);
115724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/data", PROFILE_DATA);
115824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/data/#", PROFILE_DATA_ID);
115924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/as_vcard", PROFILE_AS_VCARD);
116024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts", PROFILE_RAW_CONTACTS);
116124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#",
116224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID);
116324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/data",
116424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID_DATA);
116524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/entity",
116624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID_ENTITIES);
116746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
11683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items", STREAM_ITEMS);
11693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/photo", STREAM_ITEMS_PHOTOS);
11703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#", STREAM_ITEMS_ID);
11713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#/photo", STREAM_ITEMS_ID_PHOTOS);
11723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#/photo/#",
11733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                STREAM_ITEMS_ID_PHOTOS_ID);
11743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items_limit", STREAM_ITEMS_LIMIT);
11753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
1176f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "display_photo/*", DISPLAY_PHOTO);
1177f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "photo_dimensions", PHOTO_DIMENSIONS);
1178f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
117946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        HashMap<String, Integer> tmpTypeMap = new HashMap<String, Integer>();
118046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_CALL, DataUsageStatColumns.USAGE_TYPE_INT_CALL);
118146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_LONG_TEXT,
118246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT);
118346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_SHORT_TEXT,
118446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DataUsageStatColumns.USAGE_TYPE_INT_SHORT_TEXT);
118546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        sDataUsageTypeMap = Collections.unmodifiableMap(tmpTypeMap);
118619a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    }
118719a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
1188d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static class DirectoryInfo {
1189d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String authority;
1190d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountName;
1191d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountType;
1192d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
1193d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
1194d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
1195d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Cached information about contact directories.
1196d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
11974458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private HashMap<String, DirectoryInfo> mDirectoryCache = new HashMap<String, DirectoryInfo>();
11984458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private boolean mDirectoryCacheValid = false;
1199d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
12003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
120143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * An entry in group id cache. It maps the combination of (account type, account name, data set,
1202ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * and source id) to group row id.
1203ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     */
1204e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    public static class GroupIdCacheEntry {
1205ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType;
1206ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName;
120743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String dataSet;
1208ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String sourceId;
1209ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        long groupId;
1210ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    }
1211a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1212e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // We don't need a soft cache for groups - the assumption is that there will only
1213e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // be a small number of contact groups. The cache is keyed off source id.  The value
1214e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // is a list of groups with this group id.
1215e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    private HashMap<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache = Maps.newHashMap();
1216e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov
121724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
121824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Cached information about the contact ID and raw contact IDs that make up the user's
121924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * profile entry.
122024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
122124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static class ProfileIdCache {
122224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        boolean inited;
122324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        long profileContactId;
122424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        Set<Long> profileRawContactIds = Sets.newHashSet();
122524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        Set<Long> profileDataIds = Sets.newHashSet();
122624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
122724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        /**
122824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         * Initializes the cache of profile contact and raw contact IDs.  Does nothing if
122924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         * the cache is already initialized (unless forceRefresh is set to true).
123024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         * @param db The contacts database.
123124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         * @param forceRefresh Whether to force re-initialization of the cache.
123224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         */
123324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        private void init(SQLiteDatabase db, boolean forceRefresh) {
123424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            if (!inited || forceRefresh) {
123524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                profileContactId = 0;
123624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                profileRawContactIds.clear();
123724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                profileDataIds.clear();
123824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                Cursor c = db.rawQuery("SELECT " +
123924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        RawContactsColumns.CONCRETE_CONTACT_ID + "," +
124024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        RawContactsColumns.CONCRETE_ID + "," +
124124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        DataColumns.CONCRETE_ID +
124224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        " FROM " + Tables.RAW_CONTACTS + " JOIN " + Tables.ACCOUNTS + " ON " +
124324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        RawContactsColumns.CONCRETE_ID + "=" +
124424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        AccountsColumns.PROFILE_RAW_CONTACT_ID +
124524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        " JOIN " + Tables.DATA + " ON " +
124624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        RawContactsColumns.CONCRETE_ID + "=" + DataColumns.CONCRETE_RAW_CONTACT_ID,
124724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        null);
124824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                try {
124924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    while (c.moveToNext()) {
125024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        if (profileContactId == 0) {
125124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                            profileContactId = c.getLong(0);
125224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        }
125324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        profileRawContactIds.add(c.getLong(1));
125424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        profileDataIds.add(c.getLong(2));
125524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    }
125624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                } finally {
125724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    c.close();
125824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                }
125924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
126024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
126124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
126224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
126324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private ProfileIdCache mProfileIdCache;
126424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1265f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
1266f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Maximum dimension (height or width) of display photos.  Larger images will be scaled
1267f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * to fit.
1268f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
1269f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private int mMaxDisplayPhotoDim;
1270f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1271f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
1272f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Maximum dimension (height or width) of photo thumbnails.
1273f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
1274f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private int mMaxThumbnailPhotoDim;
1275f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
12763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private HashMap<String, DataRowHandler> mDataRowHandlers;
1277b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private ContactsDatabaseHelper mDbHelper;
127831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
1279f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private PhotoStore mPhotoStore;
1280f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
12814097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
1282f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private NameLookupBuilder mNameLookupBuilder;
1283315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
1284622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private PostalSplitter mPostalSplitter;
1285622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
128672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    private ContactDirectoryManager mContactDirectoryManager;
1287622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private ContactAggregator mContactAggregator;
1288f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
1289a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    private GlobalSearchSupport mGlobalSearchSupport;
1290d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov    private CommonNicknameCache mCommonNicknameCache;
1291f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    private SearchIndexManager mSearchIndexManager;
1292a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
129320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private ContentValues mValues = new ContentValues();
129473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    private HashMap<String, Boolean> mAccountWritability = Maps.newHashMap();
129520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
129609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private int mProviderStatus = ProviderStatus.STATUS_NORMAL;
12973826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private boolean mProviderStatusUpdateNeeded;
129809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private long mEstimatedStorageRequirement = 0;
129915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private volatile CountDownLatch mReadAccessLatch;
130015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private volatile CountDownLatch mWriteAccessLatch;
130115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private boolean mAccountUpdateListenerRegistered;
1302bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private boolean mOkToOpenAccess = true;
130373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
1304d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov    private TransactionContext mTransactionContext = new TransactionContext();
1305de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov
13061a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey    private boolean mVisibleTouched = false;
13071a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
130881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    private boolean mSyncToNetwork;
130981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
13104cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    private Locale mCurrentLocale;
13113826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private int mContactsAccountCount;
1312d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov
1313bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private HandlerThread mBackgroundThread;
1314bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private Handler mBackgroundHandler;
1315bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1316f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private long mLastPhotoCleanup = 0;
1317f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
13184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
13194f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
1320de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        super.onCreate();
1321ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        try {
1322ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return initialize();
1323ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        } catch (RuntimeException e) {
1324ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            Log.e(TAG, "Cannot start provider", e);
1325ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return false;
1326ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        }
1327ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    }
132835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1329ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    private boolean initialize() {
133015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        StrictMode.setThreadPolicy(
133115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
133215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
13333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Resources resources = getContext().getResources();
1334f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mMaxDisplayPhotoDim = resources.getInteger(
1335f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                R.integer.config_max_display_photo_dim);
1336f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mMaxThumbnailPhotoDim = resources.getInteger(
1337f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                R.integer.config_max_thumbnail_photo_dim);
13383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
133924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        mProfileIdCache = new ProfileIdCache();
1340b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper = (ContactsDatabaseHelper)getDatabaseHelper();
134172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        mContactDirectoryManager = new ContactDirectoryManager(this);
1342a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mGlobalSearchSupport = new GlobalSearchSupport(this);
1343f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mPhotoStore = new PhotoStore(getContext().getFilesDir(), mDbHelper);
134465ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov
1345bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        // The provider is closed for business until fully initialized
134615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mReadAccessLatch = new CountDownLatch(1);
134715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mWriteAccessLatch = new CountDownLatch(1);
134872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
1349bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundThread = new HandlerThread("ContactsProviderWorker",
1350bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                Process.THREAD_PRIORITY_BACKGROUND);
1351bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundThread.start();
1352bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler = new Handler(mBackgroundThread.getLooper()) {
1353bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            @Override
1354bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            public void handleMessage(Message msg) {
1355bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                performBackgroundTask(msg.what, msg.obj);
1356bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1357bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        };
13582a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov
135915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_INITIALIZE);
1360bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS);
1361bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
1362bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_LOCALE);
1363bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM);
136405e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_SEARCH_INDEX);
1365bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_PROVIDER_STATUS);
136615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_OPEN_WRITE_ACCESS);
1367f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
13683826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
136949d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov        return true;
13704f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
13714f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1372767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    /**
137351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * (Re)allocates all locale-sensitive structures.
137451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
137504b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov    private void initForDefaultLocale() {
137615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        Context context = getContext();
137715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mLegacyApiSupport = new LegacyApiSupport(context, mDbHelper, this, mGlobalSearchSupport);
13784cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mCurrentLocale = getLocale();
137904b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov        mNameSplitter = mDbHelper.createNameSplitter();
13804cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
13814cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mPostalSplitter = new PostalSplitter(mCurrentLocale);
138251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        mCommonNicknameCache = new CommonNicknameCache(mDbHelper.getReadableDatabase());
1383cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao        ContactLocaleUtils.getIntance().setLocale(mCurrentLocale);
13845b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator = new ContactAggregator(this, mDbHelper,
138515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                createPhotoPriorityResolver(context), mNameSplitter, mCommonNicknameCache);
13865b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
1387f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov        mSearchIndexManager = new SearchIndexManager(this);
13885b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
1389bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers = new HashMap<String, DataRowHandler>();
1390bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1391bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Email.CONTENT_ITEM_TYPE,
13926d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForEmail(context, mDbHelper, mContactAggregator));
1393bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Im.CONTENT_ITEM_TYPE,
13946d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForIm(context, mDbHelper, mContactAggregator));
1395bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Organization.CONTENT_ITEM_TYPE,
13966d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForOrganization(context, mDbHelper, mContactAggregator));
1397bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Phone.CONTENT_ITEM_TYPE,
13986d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForPhoneNumber(context, mDbHelper, mContactAggregator));
1399bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE,
14006d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForNickname(context, mDbHelper, mContactAggregator));
1401bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(StructuredName.CONTENT_ITEM_TYPE,
14026d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForStructuredName(context, mDbHelper, mContactAggregator,
1403bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mNameSplitter, mNameLookupBuilder));
1404bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(StructuredPostal.CONTENT_ITEM_TYPE,
14056d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForStructuredPostal(context, mDbHelper, mContactAggregator,
1406bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mPostalSplitter));
1407bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(GroupMembership.CONTENT_ITEM_TYPE,
14086d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForGroupMembership(context, mDbHelper, mContactAggregator,
1409bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mGroupIdCache));
1410bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Photo.CONTENT_ITEM_TYPE,
1411f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                new DataRowHandlerForPhoto(context, mDbHelper, mContactAggregator, mPhotoStore));
14126d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov        mDataRowHandlers.put(Note.CONTENT_ITEM_TYPE,
14136d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForNote(context, mDbHelper, mContactAggregator));
1414bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1415bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1416bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    /**
1417bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     * Visible for testing.
1418bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     */
1419bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    /* package */ PhotoPriorityResolver createPhotoPriorityResolver(Context context) {
1420bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        return new PhotoPriorityResolver(context);
1421bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1422bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1423bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task) {
1424bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendEmptyMessage(task);
1425bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1426bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1427bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task, Object arg) {
1428bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendMessage(mBackgroundHandler.obtainMessage(task, arg));
1429bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1430bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1431bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void performBackgroundTask(int task, Object arg) {
1432bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        switch (task) {
143315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_INITIALIZE: {
143415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                initForDefaultLocale();
143515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch.countDown();
143615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch = null;
143715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                break;
143815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            }
143915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
144015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_OPEN_WRITE_ACCESS: {
1441bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (mOkToOpenAccess) {
144215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch.countDown();
144315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch = null;
1444bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1445bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1446bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1447bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1448bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS: {
1449bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (isLegacyContactImportNeeded()) {
1450bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    importLegacyContactsInBackground();
1451bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1452bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1453bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1454bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1455bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_ACCOUNTS: {
145615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Context context = getContext();
145715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                if (!mAccountUpdateListenerRegistered) {
145815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    AccountManager.get(context).addOnAccountsUpdatedListener(this, null, false);
145915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mAccountUpdateListenerRegistered = true;
146015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                }
146115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
146215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Account[] accounts = AccountManager.get(context).getAccounts();
1463bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                boolean accountsChanged = updateAccountsInBackground(accounts);
1464bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateContactsAccountCount(accounts);
1465bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateDirectoriesInBackground(accountsChanged);
1466bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1467bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1468bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1469bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_LOCALE: {
1470bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateLocaleInBackground();
1471bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1472bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1473bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1474fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            case BACKGROUND_TASK_CHANGE_LOCALE: {
1475fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                changeLocaleInBackground();
1476fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                break;
1477fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            }
1478fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1479bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM: {
1480bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (isAggregationUpgradeNeeded()) {
1481bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    upgradeAggregationAlgorithmInBackground();
1482bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1483bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1484bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1485bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
148605e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_SEARCH_INDEX: {
148705e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                updateSearchIndexInBackground();
148805e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                break;
148905e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            }
149005e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
1491bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_PROVIDER_STATUS: {
1492bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateProviderStatus();
1493bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1494bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1495bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1496bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_DIRECTORIES: {
1497bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (arg != null) {
1498bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    mContactDirectoryManager.onPackageChanged((String) arg);
1499bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1500bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1501bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1502f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1503f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case BACKGROUND_TASK_CLEANUP_PHOTOS: {
1504f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Check rate limit.
1505f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long now = System.currentTimeMillis();
1506f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (now - mLastPhotoCleanup > PHOTO_CLEANUP_RATE_LIMIT) {
1507f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    mLastPhotoCleanup = now;
1508f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    cleanupPhotoStore();
1509f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    break;
1510f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
1511f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1512bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
15134cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
15144cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao
151553fac8f99f3884c372c907a76766d27fa9e1d95fDmitri Plotnikov    public void onLocaleChanged() {
15163826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
15173826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
15184f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov            return;
15194f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov        }
15204f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov
1521fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_CHANGE_LOCALE);
15224cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
152351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
152451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    /**
152551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * Verifies that the contacts database is properly configured for the current locale.
152651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * If not, changes the database locale to the current locale using an asynchronous task.
152751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * This needs to be done asynchronously because the process involves rebuilding
152851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * large data structures (name lookup, sort keys), which can take minutes on
152951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * a large set of contacts.
153051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
1531bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateLocaleInBackground() {
1532f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
1533f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        // The process is already running - postpone the change
1534f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        if (mProviderStatus == ProviderStatus.STATUS_CHANGING_LOCALE) {
1535f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov            return;
1536f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        }
1537f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
153851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
153951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final String providerLocale = prefs.getString(PREF_LOCALE, null);
154051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final Locale currentLocale = mCurrentLocale;
154151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        if (currentLocale.toString().equals(providerLocale)) {
154251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            return;
154351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        }
154451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
154551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        int providerStatus = mProviderStatus;
154651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_CHANGING_LOCALE);
1547bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDbHelper.setLocale(this, currentLocale);
1548bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        prefs.edit().putString(PREF_LOCALE, currentLocale.toString()).apply();
1549bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        setProviderStatus(providerStatus);
1550bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
155151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
1552fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    /**
1553fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     * Reinitializes the provider for a new locale.
1554fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     */
1555fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    private void changeLocaleInBackground() {
1556fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Re-initializing the provider without stopping it.
1557fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Locking the database will prevent inserts/updates/deletes from
1558fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // running at the same time, but queries may still be running
1559fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // on other threads. Those queries may return inconsistent results.
1560fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        SQLiteDatabase db = mDbHelper.getWritableDatabase();
1561fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        db.beginTransaction();
1562fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        try {
1563fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            initForDefaultLocale();
1564fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.setTransactionSuccessful();
1565fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        } finally {
1566fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.endTransaction();
1567fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        }
1568fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1569fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        updateLocaleInBackground();
1570fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    }
1571fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
157205e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    protected void updateSearchIndexInBackground() {
157305e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        mSearchIndexManager.updateIndex();
157405e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    }
157505e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
1576bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateDirectoriesInBackground(boolean rescan) {
1577bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mContactDirectoryManager.scanAllPackages(rescan);
157851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
157951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
15803826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateProviderStatus() {
15813826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
15823826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
15833826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return;
15843826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
15853826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
15863826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mContactsAccountCount == 0
158749d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                && DatabaseUtils.queryNumEntries(mDbHelper.getReadableDatabase(),
158849d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                        Tables.CONTACTS, null) == 0) {
15893826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            setProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS);
15903826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } else {
15913826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            setProviderStatus(ProviderStatus.STATUS_NORMAL);
15923826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
15933826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
15943826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
159531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
1596f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    protected void cleanupPhotoStore() {
15976802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        SQLiteDatabase db = mDbHelper.getWritableDatabase();
15986802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
15996802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Assemble the set of photo store file IDs that are in use, and send those to the photo
1600f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // store.  Any photos that aren't in that set will be deleted, and any photos that no
1601f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // longer exist in the photo store will be returned for us to clear out in the DB.
16026802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Cursor c = db.query(Views.DATA, new String[]{Data._ID, Photo.PHOTO_FILE_ID},
1603f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                Data.MIMETYPE + "=" + Photo.MIMETYPE + " AND "
1604f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        + Photo.PHOTO_FILE_ID + " IS NOT NULL", null, null, null, null);
16056802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Set<Long> usedPhotoFileIds = Sets.newHashSet();
16066802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> photoFileIdToDataId = Maps.newHashMap();
1607f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        try {
1608f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            while (c.moveToNext()) {
16096802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long dataId = c.getLong(0);
16106802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long photoFileId = c.getLong(1);
16116802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                usedPhotoFileIds.add(photoFileId);
16126802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                photoFileIdToDataId.put(photoFileId, dataId);
16136802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
16146802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        } finally {
16156802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            c.close();
16166802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
16176802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
16186802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Also query for all social stream item photos.
16196802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        c = db.query(Tables.STREAM_ITEM_PHOTOS,
16206802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                new String[]{
16216802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        StreamItemPhotos._ID,
16226802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        StreamItemPhotos.STREAM_ITEM_ID,
16236802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        StreamItemPhotos.PHOTO_FILE_ID
16246802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                },
16256802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                null, null, null, null, null);
16266802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> photoFileIdToStreamItemPhotoId = Maps.newHashMap();
16276802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> streamItemPhotoIdToStreamItemId = Maps.newHashMap();
16286802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        try {
16296802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            while (c.moveToNext()) {
16306802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long streamItemPhotoId = c.getLong(0);
16316802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long streamItemId = c.getLong(1);
16326802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long photoFileId = c.getLong(2);
16336802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                usedPhotoFileIds.add(photoFileId);
16346802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                photoFileIdToStreamItemPhotoId.put(photoFileId, streamItemPhotoId);
16356802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                streamItemPhotoIdToStreamItemId.put(streamItemPhotoId, streamItemId);
1636f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1637f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } finally {
1638f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            c.close();
1639f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
1640f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1641f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // Run the photo store cleanup.
16426802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Set<Long> missingPhotoIds = mPhotoStore.cleanup(usedPhotoFileIds);
1643f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1644f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // If any of the keys we're using no longer exist, clean them up.
16456802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (!missingPhotoIds.isEmpty()) {
1646f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            ArrayList<ContentProviderOperation> ops = Lists.newArrayList();
16476802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            for (long missingPhotoId : missingPhotoIds) {
16486802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (photoFileIdToDataId.containsKey(missingPhotoId)) {
16496802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    long dataId = photoFileIdToDataId.get(missingPhotoId);
1650f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    ContentValues updateValues = new ContentValues();
1651f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    updateValues.putNull(Photo.PHOTO_FILE_ID);
1652f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    ops.add(ContentProviderOperation.newUpdate(
16536802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            ContentUris.withAppendedId(Data.CONTENT_URI, dataId))
1654f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            .withValues(updateValues).build());
1655f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
16566802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (photoFileIdToStreamItemPhotoId.containsKey(missingPhotoId)) {
16576802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // For missing photos that were in stream item photos, just delete the stream
16586802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // item photo.
16596802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    long streamItemPhotoId = photoFileIdToStreamItemPhotoId.get(missingPhotoId);
16606802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    long streamItemId = streamItemPhotoIdToStreamItemId.get(streamItemPhotoId);
16616802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    ops.add(ContentProviderOperation.newDelete(
16626802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            StreamItems.CONTENT_URI.buildUpon()
16636802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    .appendPath(String.valueOf(streamItemId))
16646802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    .appendPath(StreamItems.StreamItemPhotos.CONTENT_DIRECTORY)
16656802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    .appendPath(String.valueOf(streamItemPhotoId))
16666802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    .build()).build());
16676802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                }
1668f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1669f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            try {
1670f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                applyBatch(ops);
1671f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            } catch (OperationApplicationException oae) {
1672f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Not a fatal problem (and we'll try again on the next cleanup).
1673f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                Log.e(TAG, "Failed to clean up outdated photo references", oae);
1674f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1675f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
1676f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
1677f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1678f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /* Visible for testing */
1679de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    @Override
1680b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
1681b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        return ContactsDatabaseHelper.getInstance(context);
168231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
168331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
1684f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /* package */ PhotoStore getPhotoStore() {
1685f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        return mPhotoStore;
1686f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
1687f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
168887614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    /* package */ int getMaxDisplayPhotoDim() {
168987614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro        return mMaxDisplayPhotoDim;
169087614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    }
169187614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro
169287614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    /* package */ int getMaxThumbnailPhotoDim() {
169387614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro        return mMaxThumbnailPhotoDim;
169487614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    }
169587614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro
1696013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    /* package */ NameSplitter getNameSplitter() {
1697013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        return mNameSplitter;
1698013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    }
1699013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov
17005df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    /* package */ NameLookupBuilder getNameLookupBuilder() {
17015df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov        return mNameLookupBuilder;
17025df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    }
17035df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov
17045dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    /* Visible for testing */
1705ed78fd6df5e9f3a2d572162e5d374d1f4a625bddDmitri Plotnikov    public ContactDirectoryManager getContactDirectoryManagerForTest() {
170672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        return mContactDirectoryManager;
170772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
170872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
170972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    /* Visible for testing */
17105dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    protected Locale getLocale() {
17115dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        return Locale.getDefault();
17125dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
17135dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
17143d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    protected boolean isLegacyContactImportNeeded() {
1715b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        int version = Integer.parseInt(mDbHelper.getProperty(PROPERTY_CONTACTS_IMPORTED, "0"));
1716b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        return version < PROPERTY_CONTACTS_IMPORT_VERSION;
17173d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
17183d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1719568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected LegacyContactImporter getLegacyContactImporter() {
1720568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return new LegacyContactImporter(getContext(), this);
1721568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1722568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1723568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
1724bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     * Imports legacy contacts as a background task.
1725568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
1726bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private void importLegacyContactsInBackground() {
1727bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Importing legacy contacts");
1728bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADING);
1729568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1730bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
1731bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDbHelper.setLocale(this, mCurrentLocale);
1732bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        prefs.edit().putString(PREF_LOCALE, mCurrentLocale.toString()).commit();
1733568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1734bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        LegacyContactImporter importer = getLegacyContactImporter();
1735bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        if (importLegacyContacts(importer)) {
1736bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            onLegacyContactImportSuccess();
1737bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        } else {
1738bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            onLegacyContactImportFailure();
1739bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
1740568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1741568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1742bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1743bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Unlocks the provider and declares that the import process is complete.
1744bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1745bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportSuccess() {
1746bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
1747bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)getContext().getSystemService(Context.NOTIFICATION_SERVICE);
1748bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.cancel(LEGACY_IMPORT_FAILED_NOTIFICATION);
1749bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1750b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        // Store a property in the database indicating that the conversion process succeeded
1751b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mDbHelper.setProperty(PROPERTY_CONTACTS_IMPORTED,
1752b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov                String.valueOf(PROPERTY_CONTACTS_IMPORT_VERSION));
1753bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_NORMAL);
1754bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Completed import of legacy contacts");
1755bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    }
1756bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1757bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1758bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Announces the provider status and keeps the provider locked.
1759bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1760bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportFailure() {
1761bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Context context = getContext();
1762bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
1763bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
1764bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1765bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        // Show a notification
1766bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Notification n = new Notification(android.R.drawable.stat_notify_error,
1767bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_ticker),
1768bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                System.currentTimeMillis());
1769bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.setLatestEventInfo(context,
1770bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_title),
1771bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_text),
1772bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                PendingIntent.getActivity(context, 0, new Intent(Intents.UI.LIST_DEFAULT), 0));
1773bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
1774bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1775bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.notify(LEGACY_IMPORT_FAILED_NOTIFICATION, n);
1776bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1777bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADE_OUT_OF_MEMORY);
1778bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Failed to import legacy contacts");
1779bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1780bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        // Do not let any database changes until this issue is resolved.
1781bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mOkToOpenAccess = false;
17823d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
17833d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
17843d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /* Visible for testing */
1785568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
17860e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        boolean aggregatorEnabled = mContactAggregator.isEnabled();
17873d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mContactAggregator.setEnabled(false);
17883d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        try {
1789bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            if (importer.importContacts()) {
1790bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1791bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                // TODO aggregate all newly added raw contacts
1792bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                mContactAggregator.setEnabled(aggregatorEnabled);
1793bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                return true;
1794bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
17953d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } catch (Throwable e) {
17963d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           Log.e(TAG, "Legacy contact import failed", e);
17973d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
1798bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mEstimatedStorageRequirement = importer.getEstimatedStorageRequirement();
1799bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        return false;
18003d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
18013d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1802a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
1803a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
1804a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
1805a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
1806b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper.wipeData();
1807f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mPhotoStore.clear();
18083826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatus = ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS;
1809a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1810a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1811568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
181215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov     * During intialization, this content provider will
1813568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * block all attempts to change contacts data. In particular, it will hold
1814568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * up all contact syncs. As soon as the import process is complete, all
1815568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * processes waiting to write to the provider are unblocked and can proceed
1816568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * to compete for the database transaction monitor.
1817568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
181815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private void waitForAccess(CountDownLatch latch) {
181915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (latch == null) {
182015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            return;
182115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        }
182215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
182315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        while (true) {
182415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            try {
182515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                latch.await();
182615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                return;
182715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            } catch (InterruptedException e) {
182815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Thread.currentThread().interrupt();
1829ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            }
1830568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
1831568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1832568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1833568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1834568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
183515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1836568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.insert(uri, values);
1837568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1838568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1839568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1840568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
184115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (mWriteAccessLatch != null) {
1842bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // We are stuck trying to upgrade contacts db.  The only update request
1843bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // allowed in this case is an update of provider status, which will trigger
1844bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // an attempt to upgrade contacts again.
1845bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            int match = sUriMatcher.match(uri);
1846bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            if (match == PROVIDER_STATUS) {
1847bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                Integer newStatus = values.getAsInteger(ProviderStatus.STATUS);
1848bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                if (newStatus != null && newStatus == ProviderStatus.STATUS_UPGRADING) {
1849bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    scheduleBackgroundTask(BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS);
1850bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 1;
1851bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                } else {
1852bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 0;
1853bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                }
1854bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
1855bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
185615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1857568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.update(uri, values, selection, selectionArgs);
1858568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1859568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1860568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1861568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
186215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1863568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.delete(uri, selection, selectionArgs);
1864568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1865568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1866568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1867568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
1868568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            throws OperationApplicationException {
186915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1870568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.applyBatch(operations);
1871568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1872568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
18734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
18747b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    public int bulkInsert(Uri uri, ContentValues[] values) {
18757b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov        waitForAccess(mWriteAccessLatch);
18767b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov        return super.bulkInsert(uri, values);
18777b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    }
18787b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov
18797b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    @Override
1880285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void onBeginTransaction() {
1881bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1882b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "onBeginTransaction");
1883b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1884285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.onBeginTransaction();
18851ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.clearPendingAggregations();
1886d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.clear();
1887b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1888b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1889285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1890285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
1891285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void beforeTransactionCommit() {
18921129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
1893bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1894b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "beforeTransactionCommit");
1895b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1896285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.beforeTransactionCommit();
1897b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
1898bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        mContactAggregator.aggregateInTransaction(mTransactionContext, mDb);
18991a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (mVisibleTouched) {
19001a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = false;
1901b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.updateAllVisible();
19021a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        }
19033826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
1904bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        updateSearchIndexInTransaction();
1905bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov
19063826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatusUpdateNeeded) {
19073826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
19083826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatusUpdateNeeded = false;
19093826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
1910b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1911b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1912bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov    private void updateSearchIndexInTransaction() {
1913bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        Set<Long> staleContacts = mTransactionContext.getStaleSearchIndexContactIds();
1914bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        Set<Long> staleRawContacts = mTransactionContext.getStaleSearchIndexRawContactIds();
1915bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        if (!staleContacts.isEmpty() || !staleRawContacts.isEmpty()) {
1916bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mSearchIndexManager.updateIndexForRawContacts(staleContacts, staleRawContacts);
1917bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mTransactionContext.clearSearchIndexUpdates();
1918bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        }
1919bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov    }
1920bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov
1921b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void flushTransactionalChanges() {
1922bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1923b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "flushTransactionChanges");
1924b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
19251129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
192624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // Determine whether we need to refresh the profile ID cache.
192724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        boolean profileCacheRefreshNeeded = false;
192824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1929d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        for (long rawContactId : mTransactionContext.getInsertedRawContactIds()) {
19308ab0b7a48efe540226253567bcf6fdbc487186a2Dmitri Plotnikov            mDbHelper.updateRawContactDisplayName(mDb, rawContactId);
1931bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mContactAggregator.onRawContactInsert(mTransactionContext, mDb, rawContactId);
1932285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        }
1933b5a4add17815167d20a90645779df34cdf45280dFred Quintana
193443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        Map<Long, AccountWithDataSet> insertedProfileRawContactAccountMap =
193524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                mTransactionContext.getInsertedProfileRawContactIds();
193624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (!insertedProfileRawContactAccountMap.isEmpty()) {
193724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            for (long profileRawContactId : insertedProfileRawContactAccountMap.keySet()) {
193824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                mDbHelper.updateRawContactDisplayName(mDb, profileRawContactId);
193924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                mContactAggregator.onProfileRawContactInsert(mTransactionContext, mDb,
194024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        profileRawContactId,
194124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        insertedProfileRawContactAccountMap.get(profileRawContactId));
194224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
194324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            profileCacheRefreshNeeded = true;
194424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
194524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1946d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        Set<Long> dirtyRawContacts = mTransactionContext.getDirtyRawContactIds();
1947d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!dirtyRawContacts.isEmpty()) {
1948a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
1949a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_DIRTY_SQL);
1950d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, dirtyRawContacts);
1951a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
1952a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
195324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
195424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            profileCacheRefreshNeeded = profileCacheRefreshNeeded ||
195524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    !Collections.disjoint(mProfileIdCache.profileRawContactIds, dirtyRawContacts);
1956a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        }
1957a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
1958d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        Set<Long> updatedRawContacts = mTransactionContext.getUpdatedRawContactIds();
1959d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!updatedRawContacts.isEmpty()) {
1960a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
1961a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_VERSION_SQL);
1962d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, updatedRawContacts);
1963a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
1964a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
196524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
196624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            profileCacheRefreshNeeded = profileCacheRefreshNeeded ||
196724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    !Collections.disjoint(mProfileIdCache.profileRawContactIds, updatedRawContacts);
1968b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1969b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1970d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        for (Map.Entry<Long, Object> entry : mTransactionContext.getUpdatedSyncStates()) {
1971b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long id = entry.getKey();
19729d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            if (mDbHelper.getSyncState().update(mDb, id, entry.getValue()) <= 0) {
19739d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                throw new IllegalStateException(
19749d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                        "unable to update sync state, does it still exist?");
19759d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            }
1976b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1977b5a4add17815167d20a90645779df34cdf45280dFred Quintana
197824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (profileCacheRefreshNeeded) {
197924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Force the profile ID cache to refresh.
198024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            mProfileIdCache.init(mDb, true);
198124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
198224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1983d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.clear();
1984b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1985b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1986a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /**
1987a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * Appends comma separated ids.
1988a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * @param ids Should not be empty
1989a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     */
1990d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov    private void appendIds(StringBuilder sb, Set<Long> ids) {
1991b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long id : ids) {
1992a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            sb.append(id).append(',');
1993b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1994a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
1995a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        sb.setLength(sb.length() - 1); // Yank the last comma
1996285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
1997285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
199824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
199924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Checks whether the given contact ID represents the user's personal profile - if it is, calls
200024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * a permission check (for writing the profile if forWrite is true, for reading the profile
200124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * otherwise).  If the contact ID is not the user's profile, no check is executed.
2002afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro     * @param db The database.
200324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param contactId The contact ID to be checked.
200424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forWrite Whether the caller is attempting to do a write (vs. read) operation.
200524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
2006afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro    private void enforceProfilePermissionForContact(SQLiteDatabase db, long contactId,
2007afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro            boolean forWrite) {
2008afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        mProfileIdCache.init(db, false);
200924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (mProfileIdCache.profileContactId == contactId) {
201024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermission(forWrite);
201124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
201224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
201324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
201424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
201524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Checks whether the given raw contact ID is a member of the user's personal profile - if it
201624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * is, calls a permission check (for writing the profile if forWrite is true, for reading the
201724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * profile otherwise).  If the raw contact ID is not in the user's profile, no check is
201824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * executed.
2019afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro     * @param db The database.
202024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param rawContactId The raw contact ID to be checked.
202124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forWrite Whether the caller is attempting to do a write (vs. read) operation.
202224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
2023afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro    private void enforceProfilePermissionForRawContact(SQLiteDatabase db, long rawContactId,
2024afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro            boolean forWrite) {
2025afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        mProfileIdCache.init(db, false);
202624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (mProfileIdCache.profileRawContactIds.contains(rawContactId)) {
202724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermission(forWrite);
202824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
202924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
203024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
203124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
203224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Checks whether the given data ID is a member of the user's personal profile - if it is,
203324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * calls a permission check (for writing the profile if forWrite is true, for reading the
203424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * profile otherwise).  If the data ID is not in the user's profile, no check is executed.
2035afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro     * @param db The database.
203624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param dataId The data ID to be checked.
203724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forWrite Whether the caller is attempting to do a write (vs. read) operation.
203824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
2039afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro    private void enforceProfilePermissionForData(SQLiteDatabase db, long dataId, boolean forWrite) {
2040afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        mProfileIdCache.init(db, false);
204124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (mProfileIdCache.profileDataIds.contains(dataId)) {
204224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermission(forWrite);
204324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
204424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
204524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
204624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
204724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Performs a permission check for WRITE_PROFILE or READ_PROFILE (depending on the parameter).
204824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * If the permission check fails, this will throw a SecurityException.
204924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forWrite Whether the caller is attempting to do a write (vs. read) operation.
205024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
205124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private void enforceProfilePermission(boolean forWrite) {
205224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        String profilePermission = forWrite
205324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                ? "android.permission.WRITE_PROFILE"
205424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                : "android.permission.READ_PROFILE";
205524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        getContext().enforceCallingOrSelfPermission(profilePermission, null);
205624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
205724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2058285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2059cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    protected void notifyChange() {
206081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        notifyChange(mSyncToNetwork);
206181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = false;
206281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
206381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
206481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    protected void notifyChange(boolean syncToNetwork) {
206581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
206681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                syncToNetwork);
2067cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    }
2068568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
206951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    protected void setProviderStatus(int status) {
20703826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != status) {
20713826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatus = status;
20723826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            getContext().getContentResolver().notifyChange(ProviderStatus.CONTENT_URI, null, false);
20733826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
207451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
207551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
2076f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    public DataRowHandler getDataRowHandler(final String mimeType) {
20773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
20783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
20796d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov            handler = new DataRowHandlerForCustomMimetype(
20806d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                    getContext(), mDbHelper, mContactAggregator, mimeType);
20813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
20823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
20833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
20843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
20853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
20864f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2087de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected Uri insertInTransaction(Uri uri, ContentValues values) {
2088bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
20891129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Log.v(TAG, "insertInTransaction: " + uri + " " + values);
2090b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2091f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2092f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2093f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2094f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2095a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
2096a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
209735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2098a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
209935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2100b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                id = mDbHelper.getSyncState().insert(mDb, values);
210135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
210235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2103d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
2104d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                insertContact(values);
21056bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
21066bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
21076bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
210824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
210924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                throw new UnsupportedOperationException(
211024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        "The profile contact is created automatically");
211124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
211224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
21135ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
211424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                id = insertRawContact(uri, values, callerIsSyncAdapter, false);
2115f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2116a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2117a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2118a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
21195ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
21205ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(1));
2121f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2122f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2123a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2124a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2125a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
21263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case RAW_CONTACTS_ID_STREAM_ITEMS: {
21273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                values.put(StreamItems.RAW_CONTACT_ID, uri.getPathSegments().get(1));
21283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItem(uri, values);
21293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
21303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
21313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
21323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
213324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS: {
213424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(true);
213524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                id = insertRawContact(uri, values, callerIsSyncAdapter, true);
213624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                mSyncToNetwork |= !callerIsSyncAdapter;
213724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
213824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
213924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2140a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case DATA: {
2141f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2142f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2143a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2144a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2145a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2146ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
2147f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                id = insertGroup(uri, values, callerIsSyncAdapter);
2148f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2149ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2150ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2151ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2152eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
21535aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertSettings(uri, values);
215443880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2155eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
2156eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2157eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
215882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
215982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                id = insertStatusUpdate(values);
21601f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
21611f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
21621f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
21633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
21643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItem(uri, values);
21653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
21663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
21673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
21683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
21693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
21703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItemPhoto(uri, values);
21713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
21723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
21733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
21743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
21753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
21763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                values.put(StreamItemPhotos.STREAM_ITEM_ID, uri.getPathSegments().get(1));
21773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItemPhoto(uri, values);
21783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
21793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
21803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
21813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
2182a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
218381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
2184f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
2185a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2186a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
21877e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
21887e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
21897e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
21907e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2191de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        return ContentUris.withAppendedId(uri, id);
2192a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2193a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2194a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2195e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * If account is non-null then store it in the values. If the account is
2196e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * already specified in the values then it must be consistent with the
2197e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * account, if it is non-null.
2198e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *
2199e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param uri Current {@link Uri} being operated on.
2200e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param values {@link ContentValues} to read and possibly update.
2201e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when only one of
2202e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_NAME} or
2203e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_TYPE} is specified, leaving the
2204e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             other undefined.
2205e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when {@link RawContacts#ACCOUNT_NAME}
2206e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             and {@link RawContacts#ACCOUNT_TYPE} are inconsistent between
2207e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             the given {@link Uri} and {@link ContentValues}.
22087e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
2209e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey    private Account resolveAccount(Uri uri, ContentValues values) throws IllegalArgumentException {
2210f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
2211f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
2212e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
2213f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2214f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
2215f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
2216e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialValues = TextUtils.isEmpty(valueAccountName)
2217e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                ^ TextUtils.isEmpty(valueAccountType);
2218e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2219e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri || partialValues) {
2220e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
2221fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
2222fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
2223e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
2224e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2225e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
2226e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
2227e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validUri = !TextUtils.isEmpty(accountName);
2228e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validValues = !TextUtils.isEmpty(valueAccountName);
2229e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2230e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validValues && validUri) {
2231e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Check that accounts match when both present
2232e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            final boolean accountMatch = TextUtils.equals(accountName, valueAccountName)
2233e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    && TextUtils.equals(accountType, valueAccountType);
2234e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            if (!accountMatch) {
2235fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new IllegalArgumentException(mDbHelper.exceptionMessage(
2236fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        "When both specified, ACCOUNT_NAME and ACCOUNT_TYPE must match", uri));
2237e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            }
2238e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validUri) {
2239e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Fill values from Uri when not present
2240f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_NAME, accountName);
2241f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_TYPE, accountType);
2242e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validValues) {
2243f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountName = valueAccountName;
2244f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountType = valueAccountType;
2245e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else {
2246e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            return null;
2247f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
2248f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2249e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Use cached Account object when matches, otherwise create
2250f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mAccount == null
2251f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.name.equals(accountName)
2252f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.type.equals(accountType)) {
2253f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mAccount = new Account(accountName, accountType);
2254035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
2255f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2256e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        return mAccount;
22577e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
22587e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
22597e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
226043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * Resolves the account and builds an {@link AccountWithDataSet} based on the data set specified
226143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * in the URI or values (if any).
226243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * @param uri Current {@link Uri} being operated on.
226343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * @param values {@link ContentValues} to read and possibly update.
226443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     */
226543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    private AccountWithDataSet resolveAccountWithDataSet(Uri uri, ContentValues values) {
226643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        final Account account = resolveAccount(uri, mValues);
226743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        AccountWithDataSet accountWithDataSet = null;
226843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        if (account != null) {
226943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            String dataSet = getQueryParameter(uri, RawContacts.DATA_SET);
227043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            if (dataSet == null) {
227143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                dataSet = mValues.getAsString(RawContacts.DATA_SET);
227243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
227343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            accountWithDataSet = new AccountWithDataSet(account.name, account.type, dataSet);
227443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        }
227543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        return accountWithDataSet;
227643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    }
227743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
227843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    /**
2279d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Inserts an item in the contacts table
22806bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
22816bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
22826bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
22836bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
2284d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertContact(ContentValues values) {
2285de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregate contacts are created automatically");
22866bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
22876bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
22886bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
228924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Inserts an item in the raw contacts table
2290a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2291f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param uri the values for the new row
2292f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param values the account this contact should be associated with. may be null.
2293dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana     * @param callerIsSyncAdapter
229424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forProfile Whether this raw contact is being inserted into the user's profile.
2295a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2296a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
229724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private long insertRawContact(Uri uri, ContentValues values, boolean callerIsSyncAdapter,
229824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            boolean forProfile) {
2299f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2300f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2301f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2302f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
230343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        AccountWithDataSet accountWithDataSet = resolveAccountWithDataSet(uri, mValues);
23047e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
23053d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
23063d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
2307f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
23083d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
23093d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2310f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long rawContactId = mDb.insert(Tables.RAW_CONTACTS, RawContacts.CONTACT_ID, mValues);
2311f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        int aggregationMode = RawContacts.AGGREGATION_MODE_DEFAULT;
231224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (forProfile) {
231324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Profile raw contacts should never be aggregated by the aggregator; they are always
231424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // aggregated under a single profile contact.
231524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            aggregationMode = RawContacts.AGGREGATION_MODE_DISABLED;
231624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        } else if (mValues.containsKey(RawContacts.AGGREGATION_MODE)) {
2317f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            aggregationMode = mValues.getAsInteger(RawContacts.AGGREGATION_MODE);
2318f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        }
2319f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        mContactAggregator.markNewForAggregation(rawContactId, aggregationMode);
2320285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
232124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (forProfile) {
232224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Trigger creation of the user profile Contact (or association with the existing one)
232324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // at the end of the transaction.
232443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            mTransactionContext.profileRawContactInserted(rawContactId, accountWithDataSet);
232524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        } else {
232624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Trigger creation of a Contact based on this RawContact at the end of transaction
232743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            mTransactionContext.rawContactInserted(rawContactId, accountWithDataSet);
232824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
2329f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2330dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter) {
2331dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            addAutoAddMembership(rawContactId);
2332dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            final Long starred = values.getAsLong(RawContacts.STARRED);
2333dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (starred != null && starred != 0) {
2334dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateFavoritesMembership(rawContactId, starred != 0);
2335dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2336dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2337dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
23383826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
2339023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        return rawContactId;
2340a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2341a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2342dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void addAutoAddMembership(long rawContactId) {
2343dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID,
2344dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2345dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2346dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            insertDataGroupMembership(rawContactId, groupId);
2347dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2348dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2349dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2350dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private Long findGroupByRawContactId(String selection, long rawContactId) {
2351dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        Cursor c = mDb.query(Tables.GROUPS + "," + Tables.RAW_CONTACTS, PROJECTION_GROUP_ID,
2352dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection,
2353dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                new String[]{Long.toString(rawContactId)},
2354dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                null /* groupBy */, null /* having */, null /* orderBy */);
2355dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        try {
2356dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            while (c.moveToNext()) {
2357dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return c.getLong(0);
2358dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2359dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return null;
2360dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        } finally {
2361dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            c.close();
2362dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2363dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2364dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2365dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void updateFavoritesMembership(long rawContactId, boolean isStarred) {
2366dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID,
2367dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2368dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2369dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (isStarred) {
2370dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                insertDataGroupMembership(rawContactId, groupId);
2371dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
2372dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                deleteDataGroupMembership(rawContactId, groupId);
2373dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2374dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2375dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2376dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2377dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void insertDataGroupMembership(long rawContactId, long groupId) {
2378dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        ContentValues groupMembershipValues = new ContentValues();
2379dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.GROUP_ROW_ID, groupId);
2380dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.RAW_CONTACT_ID, rawContactId);
2381dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(DataColumns.MIMETYPE_ID,
2382dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                mDbHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
2383dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mDb.insert(Tables.DATA, null, groupMembershipValues);
2384dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2385dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2386dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void deleteDataGroupMembership(long rawContactId, long groupId) {
2387dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final String[] selectionArgs = {
2388dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(mDbHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE)),
2389dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(groupId),
2390dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(rawContactId)};
2391dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mDb.delete(Tables.DATA, SELECTION_GROUPMEMBERSHIP_DATA, selectionArgs);
2392dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2393dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2394a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2395a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
2396a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2397a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
2398a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2399a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2400f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
2401a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
2402de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.clear();
2403de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.putAll(values);
240467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
2405de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
240620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
240724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // If the data being inserted belongs to the user's profile entry, check for the
240824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // WRITE_PROFILE permission before proceeding.
2409afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        enforceProfilePermissionForRawContact(mDb, rawContactId, true);
241024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2411de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace package with internal mapping
2412de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
2413de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (packageName != null) {
2414b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
2415de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
2416de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.RES_PACKAGE);
2417508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2418de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace mimetype with internal mapping
2419de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String mimeType = mValues.getAsString(Data.MIMETYPE);
2420de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (TextUtils.isEmpty(mimeType)) {
2421de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
2422de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
24234097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
2424b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.getMimeTypeId(mimeType));
2425de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
2426a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2427a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
2428d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        id = rowHandler.insert(mDb, mTransactionContext, rawContactId, mValues);
2429f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2430d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            mTransactionContext.markRawContactDirty(rawContactId);
2431a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2432d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.rawContactUpdated(rawContactId);
2433a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
24344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
24354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
24363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
24373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Inserts an item in the stream_items table.  The account is checked against the
24383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account in the raw contact for which the stream item is being inserted.  If the
24393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * new stream item results in more stream items under this raw contact than the limit,
24403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the oldest one will be deleted (note that if the stream item inserted was the
24413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * oldest, it will be immediately deleted, and this will return 0).
24423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
24433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param uri the insertion URI
24443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param values the values for the new row
24453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return the stream item _ID of the newly created row, or 0 if it was not created
24463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
24473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long insertStreamItem(Uri uri, ContentValues values) {
24483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long id = 0;
24493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.clear();
24503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.putAll(values);
24513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long rawContactId = mValues.getAsLong(StreamItems.RAW_CONTACT_ID);
24533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If the data being inserted belongs to the user's profile entry, check for the
24553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // WRITE_PROFILE permission before proceeding.
2456afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        enforceProfilePermissionForRawContact(mDb, rawContactId, true);
24573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Ensure that the raw contact exists and belongs to the caller's account.
24593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, mValues);
24603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccount(account, rawContactId);
24613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24626802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to insert accounts params - they don't exist in the stream items table.
24636802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        mValues.remove(RawContacts.ACCOUNT_NAME);
24646802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        mValues.remove(RawContacts.ACCOUNT_TYPE);
24656802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
24663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Insert the new stream item.
24676802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        id = mDb.insert(Tables.STREAM_ITEMS, null, mValues);
24686802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (id == -1) {
24696802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Insertion failed.
24706802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return 0;
24716802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
24723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check to see if we're over the limit for stream items under this raw contact.
24743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // It's possible that the inserted stream item is older than the the existing
24753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // ones, in which case it may be deleted immediately (resetting the ID to 0).
24763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        id = cleanUpOldStreamItems(rawContactId, id);
24773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return id;
24793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
24803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
24823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Inserts an item in the stream_item_photos table.  The account is checked against
24833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the account in the raw contact that owns the stream item being modified.
24843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
24853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param uri the insertion URI
24863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param values the values for the new row
24876802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @return the stream item photo _ID of the newly created row, or 0 if there was an issue
24886802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     *     with processing the photo or creating the row
24893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
24903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long insertStreamItemPhoto(Uri uri, ContentValues values) {
24913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long id = 0;
24923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.clear();
24933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.putAll(values);
24943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long streamItemId = mValues.getAsLong(StreamItemPhotos.STREAM_ITEM_ID);
24963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        if (streamItemId != 0) {
24973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            long rawContactId = lookupRawContactIdForStreamId(streamItemId);
24983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            // If the data being inserted belongs to the user's profile entry, check for the
25003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            // WRITE_PROFILE permission before proceeding.
2501afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro            enforceProfilePermissionForRawContact(mDb, rawContactId, true);
25023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
25033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            // Ensure that the raw contact exists and belongs to the caller's account.
25043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            Account account = resolveAccount(uri, mValues);
25053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            enforceModifyingAccount(account, rawContactId);
25063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
25076802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Don't attempt to insert accounts params - they don't exist in the stream item
25086802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // photos table.
25096802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            mValues.remove(RawContacts.ACCOUNT_NAME);
25106802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            mValues.remove(RawContacts.ACCOUNT_TYPE);
25113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
25126802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Process the photo and store it.
25136802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            if (processStreamItemPhoto(mValues, false)) {
25146802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // Insert the stream item photo.
25156802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                id = mDb.insert(Tables.STREAM_ITEM_PHOTOS, null, mValues);
25166802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
25173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
25183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return id;
25193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
25203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
25213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
25226802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * Processes the photo contained in the {@link ContactsContract.StreamItemPhotos#PHOTO}
25236802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * field of the given values, attempting to store it in the photo store.  If successful,
25246802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * the resulting photo file ID will be added to the values for insert/update in the table.
25256802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * <p>
25266802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * If updating, it is valid for the picture to be empty or unspecified (the function will
25276802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * still return true).  If inserting, a valid picture must be specified.
25286802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @param values The content values provided by the caller.
25296802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @param forUpdate Whether this photo is being processed for update (vs. insert).
25306802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @return Whether the insert or update should proceed.
25316802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     */
25326802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    private boolean processStreamItemPhoto(ContentValues values, boolean forUpdate) {
25336802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (!values.containsKey(StreamItemPhotos.PHOTO)) {
25346802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return forUpdate;
25356802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
25366802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        byte[] photoBytes = values.getAsByteArray(StreamItemPhotos.PHOTO);
25376802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (photoBytes == null) {
25386802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return forUpdate;
25396802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
25406802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
25416802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Process the photo and store it.
25426802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        try {
25436802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            long photoFileId = mPhotoStore.insert(new PhotoProcessor(photoBytes,
25441dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                    mMaxDisplayPhotoDim, mMaxThumbnailPhotoDim, true), true);
25456802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            if (photoFileId != 0) {
25466802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                values.put(StreamItemPhotos.PHOTO_FILE_ID, photoFileId);
25476802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                values.remove(StreamItemPhotos.PHOTO);
25486802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                return true;
25496802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            } else {
25506802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // Couldn't store the photo, return 0.
25516802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                Log.e(TAG, "Could not process stream item photo for insert");
25526802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                return false;
25536802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
25546802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        } catch (IOException ioe) {
25556802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            Log.e(TAG, "Could not process stream item photo for insert", ioe);
25566802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return false;
25576802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
25586802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    }
25596802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
25606802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    /**
25613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Looks up the raw contact ID that owns the specified stream item.
25623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param streamItemId The ID of the stream item.
25633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The associated raw contact ID, or -1 if no such stream item exists.
25643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
25653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long lookupRawContactIdForStreamId(long streamItemId) {
25663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long rawContactId = -1;
25673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c = mDb.query(Tables.STREAM_ITEMS, new String[]{StreamItems.RAW_CONTACT_ID},
25683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                StreamItems._ID + "=?", new String[]{String.valueOf(streamItemId)},
25693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                null, null, null);
25703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
25713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if (c.moveToFirst()) {
25723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                rawContactId = c.getLong(0);
25733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
25743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
25753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
25763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
25773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return rawContactId;
25783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
25793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
25803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
25813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given raw contact ID is owned by the given account.
25823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * If the resolved account is null, this will return true iff the raw contact
25833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * is also associated with the "null" account.
25843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
25853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * If the resolved account does not match, this will throw a security exception.
25863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
25873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param rawContactId The raw contact ID to check for.
25883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
25893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void enforceModifyingAccount(Account account, long rawContactId) {
25903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        String accountSelection = RawContactsColumns.CONCRETE_ID + "=? AND "
25913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_NAME + "=? AND "
25923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + "=?";
25933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        String noAccountSelection = RawContactsColumns.CONCRETE_ID + "=? AND "
25943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " IS NULL AND "
25953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " IS NULL";
25963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c;
25973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        if (account != null) {
25983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContactsColumns.CONCRETE_ID},
25993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    accountSelection,
26003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    new String[]{String.valueOf(rawContactId), mAccount.name, mAccount.type},
26013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    null, null, null);
26023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } else {
26033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContactsColumns.CONCRETE_ID},
26043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    noAccountSelection, new String[]{String.valueOf(rawContactId)},
26053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    null, null, null);
26063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
26073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
26083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if(c.getCount() == 0) {
26093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                throw new SecurityException("Caller account does not match raw contact ID "
26103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    + rawContactId);
26113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
26123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
26133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
26143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
26153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
26163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
26173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
26183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given selection of stream items matches up with the given
26193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account.  If any of the raw contacts fail the account check, this will throw a
26203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * security exception.
26213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
26223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selection The selection.
26233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selectionArgs The selection arguments.
26243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The list of stream item IDs that would be included in this selection.
26253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
26263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private List<Long> enforceModifyingAccountForStreamItems(Account account, String selection,
26273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
26283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemIds = Lists.newArrayList();
26293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
26303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        setTablesAndProjectionMapForStreamItems(qb);
26313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c = qb.query(mDb,
26323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{StreamItems._ID, StreamItems.RAW_CONTACT_ID},
26333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selection, selectionArgs, null, null, null);
26343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
26353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            while (c.moveToNext()) {
26363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                streamItemIds.add(c.getLong(0));
26373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
26383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Throw a security exception if the account doesn't match the raw contact's.
26393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                enforceModifyingAccount(account, c.getLong(1));
26403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
26413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
26423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
26433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
26443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemIds;
26453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
26463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
26473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
26483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given selection of stream item photos matches up with the given
26493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account.  If any of the raw contacts fail the account check, this will throw a
26503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * security exception.
26513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
26523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selection The selection.
26533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selectionArgs The selection arguments.
26543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The list of stream item photo IDs that would be included in this selection.
26553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
26563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private List<Long> enforceModifyingAccountForStreamItemPhotos(Account account, String selection,
26573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
26583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemPhotoIds = Lists.newArrayList();
26593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
26603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        setTablesAndProjectionMapForStreamItemPhotos(qb);
26613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c = qb.query(mDb, new String[]{StreamItemPhotos._ID, StreamItems.RAW_CONTACT_ID},
26623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selection, selectionArgs, null, null, null);
26633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
26643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            while (c.moveToNext()) {
26653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                streamItemPhotoIds.add(c.getLong(0));
26663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
26673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Throw a security exception if the account doesn't match the raw contact's.
26683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                enforceModifyingAccount(account, c.getLong(1));
26693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
26703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
26713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
26723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
26733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemPhotoIds;
26743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
26753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
26763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
26773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Queries the database for stream items under the given raw contact.  If there are
26783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * more entries than {@link ContactsProvider2#MAX_STREAM_ITEMS_PER_RAW_CONTACT},
26793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the oldest entries (as determined by timestamp) will be deleted.
26803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param rawContactId The raw contact ID to examine for stream items.
26813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param insertedStreamItemId The ID of the stream item that was just inserted,
26823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     prompting this cleanup.  Callers may pass 0 if no insertion prompted the
26833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     cleanup.
26843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The ID of the inserted stream item if it still exists after cleanup;
26853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     0 otherwise.
26863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
26873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long cleanUpOldStreamItems(long rawContactId, long insertedStreamItemId) {
26883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long postCleanupInsertedStreamId = insertedStreamItemId;
26893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c = mDb.query(Tables.STREAM_ITEMS, new String[]{StreamItems._ID},
26903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                StreamItems.RAW_CONTACT_ID + "=?", new String[]{String.valueOf(rawContactId)},
26913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                null, null, StreamItems.TIMESTAMP + " DESC, " + StreamItems._ID + " DESC");
26923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
26933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            int streamItemCount = c.getCount();
26943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if (streamItemCount <= MAX_STREAM_ITEMS_PER_RAW_CONTACT) {
26953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Still under the limit - nothing to clean up!
26963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return insertedStreamItemId;
26973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            } else {
26983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                c.moveToLast();
26993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                while (c.getPosition() >= MAX_STREAM_ITEMS_PER_RAW_CONTACT) {
27003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    long streamItemId = c.getLong(0);
27013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    if (insertedStreamItemId == streamItemId) {
27023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        // The stream item just inserted is being deleted.
27033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        postCleanupInsertedStreamId = 0;
27043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    }
27053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    deleteStreamItem(c.getLong(0));
27063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    c.moveToPrevious();
27073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
27083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
27093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
27103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
27113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
27123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return postCleanupInsertedStreamId;
27133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
27143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
2715ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
27168ab0b7a48efe540226253567bcf6fdbc487186a2Dmitri Plotnikov        mDbHelper.updateRawContactDisplayName(db, rawContactId);
2717d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    }
2718d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
27199261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
272020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
272120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
2722f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
272320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
272420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2725de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
2726de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
2727f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataRowHandler.DataDeleteQuery.COLUMNS,
2728f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                selection, selectionArgs, null);
2729de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        try {
2730de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            while(c.moveToNext()) {
2731f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                long rawContactId = c.getLong(DataRowHandler.DataDeleteQuery.RAW_CONTACT_ID);
273224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
273324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // Check for write profile permission if the data belongs to the profile.
2734afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForRawContact(mDb, rawContactId, true);
273524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2736f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
2737a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                DataRowHandler rowHandler = getDataRowHandler(mimeType);
2738d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                count += rowHandler.delete(mDb, mTransactionContext, c);
2739f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                if (!callerIsSyncAdapter) {
2740d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                    mTransactionContext.markRawContactDirty(rawContactId);
274188e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                }
274220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
274320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
2744de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            c.close();
274520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
274620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
274720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
274820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
274920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
275088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    /**
275188e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     * Delete a data row provided that it is one of the allowed mime types.
275288e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     */
275320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
2754f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
275588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
275688e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
27574da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(dataId);
2758f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataRowHandler.DataDeleteQuery.COLUMNS, Data._ID + "=?",
27594da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1, null);
2760f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
276120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
276220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
276320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
276420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
276520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2766f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov            String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
276720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
276820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
276920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
277020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
277120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
277220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
277320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
277420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
277520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
27767a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException("Data type mismatch: expected "
277720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
277820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
277920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
278024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Check for write profile permission if the data belongs to the profile.
278124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            long rawContactId = c.getLong(DataRowHandler.DataDeleteQuery.RAW_CONTACT_ID);
2782afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro            enforceProfilePermissionForRawContact(mDb, rawContactId, true);
278324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2784a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            DataRowHandler rowHandler = getDataRowHandler(mimeType);
2785d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            return rowHandler.delete(mDb, mTransactionContext, c);
278620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
278720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
278820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
278920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
279020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
279120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
2792ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
2793ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
2794f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private long insertGroup(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
2795f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2796f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2797f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2798e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final Account account = resolveAccount(uri, mValues);
279943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String dataSet = null;
280043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        if (account != null && mValues.containsKey(Groups.DATA_SET)) {
280143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            dataSet = mValues.getAsString(Groups.DATA_SET);
280243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        }
2803ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2804ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
2805f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String packageName = mValues.getAsString(Groups.RES_PACKAGE);
280667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
2807f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(GroupsColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
280867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
2809f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.remove(Groups.RES_PACKAGE);
2810ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2811dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final boolean isFavoritesGroup = mValues.getAsLong(Groups.FAVORITES) != null
2812dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                ? mValues.getAsLong(Groups.FAVORITES) != 0
2813dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                : false;
2814dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2815f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2816f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(Groups.DIRTY, 1);
281773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
281873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2819f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long result = mDb.insert(Tables.GROUPS, Groups.TITLE, mValues);
2820ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2821dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter && isFavoritesGroup) {
2822dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // add all starred raw contacts to this group
2823dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String selection;
2824dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs;
2825dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (account == null) {
2826dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + " IS NULL AND "
282743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + RawContacts.ACCOUNT_TYPE + " IS NULL AND "
282843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + RawContacts.DATA_SET + " IS NULL";
2829dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = null;
283043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            } else if (dataSet == null) {
2831dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + "=? AND "
2832dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + RawContacts.ACCOUNT_TYPE + "=?";
2833dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = new String[]{account.name, account.type};
283443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            } else {
283543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                selection = RawContacts.ACCOUNT_NAME + "=? AND "
283643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + RawContacts.ACCOUNT_TYPE + "=? AND "
283743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + RawContacts.DATA_SET + "=?";
283843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                selectionArgs = new String[]{account.name, account.type, dataSet};
2839dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2840dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            Cursor c = mDb.query(Tables.RAW_CONTACTS,
2841dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[]{RawContacts._ID, RawContacts.STARRED},
2842dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    selection, selectionArgs, null, null, null);
2843892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            try {
2844892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                while (c.moveToNext()) {
2845892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (c.getLong(1) != 0) {
2846892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        final long rawContactId = c.getLong(0);
2847892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        insertDataGroupMembership(rawContactId, result);
2848d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                        mTransactionContext.markRawContactDirty(rawContactId);
2849892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    }
2850dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
2851892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            } finally {
2852892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                c.close();
2853dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2854dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2855dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2856f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mValues.containsKey(Groups.GROUP_VISIBLE)) {
28571a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2858ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        }
2859ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2860ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        return result;
2861ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2862ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
28635aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private long insertSettings(Uri uri, ContentValues values) {
2864e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final long id = mDb.insert(Tables.SETTINGS, null, values);
28655aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
28661a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
28671a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2868e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
28691a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
2870e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return id;
2871e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2872e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2873ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
287482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov     * Inserts a status update.
28751f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
287682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    public long insertStatusUpdate(ContentValues values) {
287782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        final String handle = values.getAsString(StatusUpdates.IM_HANDLE);
28780a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        final Integer protocol = values.getAsInteger(StatusUpdates.PROTOCOL);
28794dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        String customProtocol = null;
28804dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
28810a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        if (protocol != null && protocol == Im.PROTOCOL_CUSTOM) {
288282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            customProtocol = values.getAsString(StatusUpdates.CUSTOM_PROTOCOL);
28834dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (TextUtils.isEmpty(customProtocol)) {
28844dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                throw new IllegalArgumentException(
28854dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                        "CUSTOM_PROTOCOL is required when PROTOCOL=PROTOCOL_CUSTOM");
28864dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
28871f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
28881f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2889dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long rawContactId = -1;
2890dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long contactId = -1;
289182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Long dataId = values.getAsLong(StatusUpdates.DATA_ID);
28926802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        String accountType = null;
28936802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        String accountName = null;
2894f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.setLength(0);
28952526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov        mSelectionArgs.clear();
2896dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        if (dataId != null) {
2897dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the contact info for the given data row.
2898dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
28992526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSb.append(Tables.DATA + "." + Data._ID + "=?");
29002526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSelectionArgs.add(String.valueOf(dataId));
29011f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
2902dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the data row to attach this presence update to
2903dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
29040a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(handle) || protocol == null) {
29050a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                throw new IllegalArgumentException("PROTOCOL and IM_HANDLE are required");
29060a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
29070a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2908dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // TODO: generalize to allow other providers to match against email
2909dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == protocol;
2910dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
29112a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov            String mimeTypeIdIm = String.valueOf(mDbHelper.getMimeTypeIdForIm());
2912dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            if (matchEmail) {
29132a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                String mimeTypeIdEmail = String.valueOf(mDbHelper.getMimeTypeIdForEmail());
2914f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
2915f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // The following hack forces SQLite to use the (mimetype_id,data1) index, otherwise
2916f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the "OR" conjunction confuses it and it switches to a full scan of
2917f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the raw_contacts table.
2918f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
2919f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // This code relies on the fact that Im.DATA and Email.DATA are in fact the same
2920f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // column - Data.DATA1
29212526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + " IN (?,?)" +
29222526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Data.DATA1 + "=?" +
29232526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND ((" + DataColumns.MIMETYPE_ID + "=? AND " + Im.PROTOCOL + "=?");
29242526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
29252526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
29262526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
29272526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
29282526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
2929dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
29302526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
29312526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
2932dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
29332526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(") OR (" + DataColumns.MIMETYPE_ID + "=?))");
29342526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
2935dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            } else {
29362526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + "=?" +
29372526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.PROTOCOL + "=?" +
29382526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.DATA + "=?");
29392526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
29402526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
29412526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
2942dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
29432526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
29442526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
2945dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
2946dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
29471f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
294882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.DATA_ID)) {
29492526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(" AND " + DataColumns.CONCRETE_ID + "=?");
29502526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(values.getAsString(StatusUpdates.DATA_ID));
2951dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
295270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
295370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
29541f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
29551f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
2956de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            cursor = mDb.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
29572526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.toString(), mSelectionArgs.toArray(EMPTY_STRING_ARRAY), null, null,
29584394086494fe7909aaca70f56fb4bb08beebf303Dmitri Plotnikov                    Clauses.CONTACT_VISIBLE + " DESC, " + Data.RAW_CONTACT_ID);
29591f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
296067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
29615ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
29626802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                accountType = cursor.getString(DataContactsQuery.ACCOUNT_TYPE);
29636802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                accountName = cursor.getString(DataContactsQuery.ACCOUNT_NAME);
2964e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
29651f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
29661f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
29671f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
29681f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
29691f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
297031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
297131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
297231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
29731f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
29741f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
297582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.PRESENCE)) {
2976a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (customProtocol == null) {
2977a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // We cannot allow a null in the custom protocol field, because SQLite3 does not
2978a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // properly enforce uniqueness of null values
2979a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                customProtocol = "";
2980a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
2981a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2982a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.clear();
298382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.DATA_ID, dataId);
2984a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.RAW_CONTACT_ID, rawContactId);
2985a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.CONTACT_ID, contactId);
298682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PROTOCOL, protocol);
298782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
298882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.IM_HANDLE, handle);
298982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.IM_ACCOUNT)) {
299082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                mValues.put(StatusUpdates.IM_ACCOUNT, values.getAsString(StatusUpdates.IM_ACCOUNT));
2991a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
299282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PRESENCE,
299382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    values.getAsString(StatusUpdates.PRESENCE));
2994aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori            mValues.put(StatusUpdates.CHAT_CAPABILITY,
2995aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                    values.getAsString(StatusUpdates.CHAT_CAPABILITY));
29961f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2997a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            // Insert the presence update
2998a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mDb.replace(Tables.PRESENCE, null, mValues);
2999a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3000e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
30010a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
300282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.STATUS)) {
300382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String status = values.getAsString(StatusUpdates.STATUS);
30040a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String resPackage = values.getAsString(StatusUpdates.STATUS_RES_PACKAGE);
30050a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Integer labelResource = values.getAsInteger(StatusUpdates.STATUS_LABEL);
30060a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
30070a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(resPackage)
30080a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && (labelResource == null || labelResource == 0)
30090a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && protocol != null) {
30100a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                labelResource = Im.getProtocolLabelResource(protocol);
30110a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
30120a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
30130a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Long iconResource = values.getAsLong(StatusUpdates.STATUS_ICON);
30140a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            // TODO compute the default icon based on the protocol
30150a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
3016a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (TextUtils.isEmpty(status)) {
301778fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                mDbHelper.deleteStatusUpdate(dataId);
3018a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            } else {
30196802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                Long timestamp = values.getAsLong(StatusUpdates.STATUS_TIMESTAMP);
30206802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (timestamp != null) {
30216802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    mDbHelper.replaceStatusUpdate(dataId, timestamp, status, resPackage,
30226802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            iconResource, labelResource);
30236802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                } else {
30246802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    mDbHelper.insertStatusUpdate(dataId, status, resPackage, iconResource,
30256802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            labelResource);
30266802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                }
30276802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
30286802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // For forward compatibility with the new stream item API, insert this status update
30296802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // there as well.  If we already have a stream item from this source, update that
30306802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // one instead of inserting a new one (since the semantics of the old status update
30316802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // API is to only have a single record).
30326802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (rawContactId != -1 && !TextUtils.isEmpty(status)) {
30336802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    ContentValues streamItemValues = new ContentValues();
30346802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RAW_CONTACT_ID, rawContactId);
30356802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.TEXT, status);
30366802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.COMMENTS, "");
30376802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_PACKAGE, resPackage);
30386802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_ICON, iconResource);
30396802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_LABEL, labelResource);
30406802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.TIMESTAMP,
30416802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            timestamp == null ? System.currentTimeMillis() : timestamp);
30426802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
30436802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // Note: The following is basically a workaround for the fact that status
30446802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // updates didn't do any sort of account enforcement, while social stream item
30456802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // updates do.  We can't expect callers of the old API to start passing account
30466802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // information along, so we just populate the account params appropriately for
304743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    // the raw contact.  Data set is not relevant here, as we only check account
304843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    // name and type.
30496802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    if (accountName != null && accountType != null) {
30506802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        streamItemValues.put(RawContacts.ACCOUNT_NAME, accountName);
30516802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        streamItemValues.put(RawContacts.ACCOUNT_TYPE, accountType);
30526802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    }
30536802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
30546802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // Check for an existing stream item from this source, and insert or update.
30556802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    Uri streamUri = StreamItems.CONTENT_URI;
30566802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    Cursor c = query(streamUri, new String[]{StreamItems._ID},
30576802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            StreamItems.RAW_CONTACT_ID + "=?",
30586802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            new String[]{String.valueOf(rawContactId)}, null);
30596802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    try {
30606802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        if (c.getCount() > 0) {
30616802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            c.moveToFirst();
30626802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            update(ContentUris.withAppendedId(streamUri, c.getLong(0)),
30636802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    streamItemValues, null, null);
30646802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        } else {
30656802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            insert(streamUri, streamItemValues);
30666802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        }
30676802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    } finally {
30686802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        c.close();
30696802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    }
30706802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
30716802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                }
3072e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            }
3073e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        }
3074bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
3075a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (contactId != -1) {
3076f4015ab9ab7c26b766b5331fbf6655b8c54877eaDmitri Plotnikov            mContactAggregator.updateLastStatusUpdateId(contactId);
3077a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3078a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3079a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        return dataId;
30801f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
30811f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
30824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3083de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
3084bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3085b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "deleteInTransaction: " + uri);
3086b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3087b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3088f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3089f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
3090508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
3091508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
309235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3093b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs);
309435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3095b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID:
3096b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3097b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3098b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
3099b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selectionWithId, selectionArgs);
3100b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3101cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            case CONTACTS: {
3102cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // TODO
3103cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return 0;
3104cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3105cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3106d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
3107d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3108dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
31096bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
31106bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
31119fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP: {
31122e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
31132e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
31142e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
3115fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
3116fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
31172e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
31182e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
31192e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
3120dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
31212e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
31222e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
31239fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP_ID: {
31249fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                // lookup contact by id and lookup key to see if they still match the actual record
31259fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final List<String> pathSegments = uri.getPathSegments();
31269fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final String lookupKey = pathSegments.get(2);
31279fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
31289fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                setTablesAndProjectionMapForContacts(lookupQb, uri, null);
3129a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
31309fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                String[] args;
31319fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                if (selectionArgs == null) {
31329fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[2];
31339fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } else {
31349fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[selectionArgs.length + 2];
31359fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
31369fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
31379fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                args[0] = String.valueOf(contactId);
313860de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann                args[1] = Uri.encode(lookupKey);
31399fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                lookupQb.appendWhere(Contacts._ID + "=? AND " + Contacts.LOOKUP_KEY + "=?");
31409fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final SQLiteDatabase db = mDbHelper.getReadableDatabase();
31419fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                Cursor c = query(db, lookupQb, null, selection, args, null, null, null);
31429fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                try {
31439fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    if (c.getCount() == 1) {
31449fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // contact was unmodified so go ahead and delete it
3145dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        return deleteContact(contactId, callerIsSyncAdapter);
31469fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    } else {
31479fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // row was changed (e.g. the merging might have changed), we got multiple
31489fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // rows or the supplied selection filtered the record out
31499fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        return 0;
31509fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    }
31519fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } finally {
31529fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    c.close();
31539fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
31549fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            }
31559fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
31562971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case RAW_CONTACTS: {
31572971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
3158fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                Cursor c = mDb.query(Tables.RAW_CONTACTS,
3159fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        new String[]{RawContacts._ID, RawContacts.CONTACT_ID},
3160e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
31612971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
31622971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
31632971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                        final long rawContactId = c.getLong(0);
3164fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        long contactId = c.getLong(1);
3165fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        numDeletes += deleteRawContact(rawContactId, contactId,
3166fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                                callerIsSyncAdapter);
31672971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
31682971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
31692971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
31702971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
31712971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
31722971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
31732971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
31745ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
31752971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                final long rawContactId = ContentUris.parseId(uri);
3176fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                return deleteRawContact(rawContactId, mDbHelper.getContactId(rawContactId),
3177fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        callerIsSyncAdapter);
3178508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3179508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
318020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
3181f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
3182944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                return deleteData(appendAccountToSelection(uri, selection), selectionArgs,
3183f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        callerIsSyncAdapter);
318420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
318520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
318648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
318748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
318848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
318948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
3190508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
3191f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
31924da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
31934da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                return deleteData(Data._ID + "=?", mSelectionArgs1, callerIsSyncAdapter);
3194ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3195ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3196ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3197f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
31985aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteGroup(uri, ContentUris.parseId(uri), callerIsSyncAdapter);
31992971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
32002971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
32012971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case GROUPS: {
32022971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
32032971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups._ID},
3204e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
32052971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
32062971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
32075aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        numDeletes += deleteGroup(uri, c.getLong(0), callerIsSyncAdapter);
32082971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
32092971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
32102971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
32112971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
321281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (numDeletes > 0) {
3213f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
321481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
32152971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
3216508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3217508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
3218eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
321943880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3220e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                return deleteSettings(uri, appendAccountToSelection(uri, selection), selectionArgs);
3221eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3222eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
322382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
32240a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                return deleteStatusUpdates(selection, selectionArgs);
32251f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
32261f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
32273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
32283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
32293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItems(uri, new ContentValues(), selection, selectionArgs);
32303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
32313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
32323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
32333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
32343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItems(uri, new ContentValues(),
32353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemsColumns.CONCRETE_ID + "=?",
32363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{uri.getLastPathSegment()});
32373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
32383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
32393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
32403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
32413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItemPhotos(uri, new ContentValues(), selection, selectionArgs);
32423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
32433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
32443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
32453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
32463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
32473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
32483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItemPhotos(uri, new ContentValues(),
32493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=? AND "
32503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                                + StreamItemPhotos.STREAM_ITEM_ID + "=?",
32513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{streamItemPhotoId, streamItemId});
32523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
32533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
325481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
325581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
32563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
325781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
3258508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
32594f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
32604f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
32611c8e40c18f92722b9bec6e8ce2e345a9828efa16Dmitri Plotnikov    public int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) {
3262ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
3263b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final long groupMembershipMimetypeId = mDbHelper
326494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
3265de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mDb.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
326694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
326794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupId, null);
326894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
326994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        try {
3270f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (callerIsSyncAdapter) {
3271de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
327294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            } else {
327394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.clear();
327494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.put(Groups.DELETED, 1);
3275f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mValues.put(Groups.DIRTY, 1);
3276de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId, null);
327794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            }
327894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        } finally {
32791a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
328094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
328194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
328294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
32835aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
3284e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.delete(Tables.SETTINGS, selection, selectionArgs);
32851a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        mVisibleTouched = true;
3286e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3287e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3288e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3289dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int deleteContact(long contactId, boolean callerIsSyncAdapter) {
3290afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        enforceProfilePermissionForContact(mDb, contactId, true);
329196b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(contactId);
3292cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
329396b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                RawContacts.CONTACT_ID + "=?", mSelectionArgs1,
329496b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                null, null, null);
3295cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        try {
3296cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            while (c.moveToNext()) {
3297cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                long rawContactId = c.getLong(0);
3298dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
3299cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3300cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } finally {
3301cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            c.close();
3302cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
3303cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
33043826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
33053826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
3306cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return mDb.delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
3307cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3308cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3309fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov    public int deleteRawContact(long rawContactId, long contactId, boolean callerIsSyncAdapter) {
3310afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        enforceProfilePermissionForRawContact(mDb, rawContactId, true);
33113389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
33123826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
33133826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
3314f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (callerIsSyncAdapter) {
331514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            mDb.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
3316fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            int count = mDb.delete(Tables.RAW_CONTACTS, RawContacts._ID + "=" + rawContactId, null);
3317fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            mContactAggregator.updateDisplayNameForContact(mDb, contactId);
3318fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return count;
331933b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } else {
3320b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.removeContactIfSingleton(rawContactId);
3321dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
332233b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        }
332333b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
332433b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
33250a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private int deleteStatusUpdates(String selection, String[] selectionArgs) {
33269705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // delete from both tables: presence and status_updates
33279705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // TODO should account type/name be appended to the where clause?
33289705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      if (VERBOSE_LOGGING) {
33299705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          Log.v(TAG, "deleting data from status_updates for " + selection);
33309705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      }
33319705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      mDb.delete(Tables.STATUS_UPDATES, getWhereClauseForStatusUpdatesTable(selection),
33329705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          selectionArgs);
33339705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      return mDb.delete(Tables.PRESENCE, selection, selectionArgs);
33340a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
33350a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
33363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItems(Uri uri, ContentValues values, String selection,
33373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
33383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // First query for the stream items to be deleted, and check that they belong
33393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // to the account.
33403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
33413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemIds = enforceModifyingAccountForStreamItems(
33423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                account, selection, selectionArgs);
33433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
33443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If no security exception has been thrown, we're fine to delete.
33453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        for (long streamItemId : streamItemIds) {
33463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            deleteStreamItem(streamItemId);
33473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
33483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
33493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mVisibleTouched = true;
33503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemIds.size();
33513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
33523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
33533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItem(long streamItemId) {
33543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Note that this does not enforce the modifying account.
33553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        deleteStreamItemPhotos(streamItemId);
33563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return mDb.delete(Tables.STREAM_ITEMS, StreamItems._ID + "=?",
33573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{String.valueOf(streamItemId)});
33583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
33593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
33603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItemPhotos(Uri uri, ContentValues values, String selection,
33613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
33623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // First query for the stream item photos to be deleted, and check that they
33633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // belong to the account.
33643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
33653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItemPhotos(account, selection, selectionArgs);
33663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
33673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If no security exception has been thrown, we're fine to delete.
33683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return mDb.delete(Tables.STREAM_ITEM_PHOTOS, selection, selectionArgs);
33693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
33703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
33713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItemPhotos(long streamItemId) {
33723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Note that this does not enforce the modifying account.
33733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return mDb.delete(Tables.STREAM_ITEM_PHOTOS, StreamItemPhotos.STREAM_ITEM_ID + "=?",
33743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{String.valueOf(streamItemId)});
33753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
33763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
3377dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int markRawContactAsDeleted(long rawContactId, boolean callerIsSyncAdapter) {
337881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = true;
337981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
3380cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.clear();
3381cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DELETED, 1);
3382cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
3383cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
3384cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
3385cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DIRTY, 1);
3386dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        return updateRawContact(rawContactId, mValues, callerIsSyncAdapter);
3387cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3388cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
33894f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3390de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
3391de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            String[] selectionArgs) {
3392bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3393b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "updateInTransaction: " + uri);
3394b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3395b5a4add17815167d20a90645779df34cdf45280dFred Quintana
339635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
339700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
339800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
3399b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (match == SYNCSTATE_ID && selection == null) {
3400b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long rowId = ContentUris.parseId(uri);
34011129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Object data = values.get(ContactsContract.SyncState.DATA);
3402d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            mTransactionContext.syncStateUpdated(rowId, data);
3403b5a4add17815167d20a90645779df34cdf45280dFred Quintana            return 1;
3404b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3405b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3406f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3407f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
340800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
340935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3410b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
3411b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        appendAccountToSelection(uri, selection), selectionArgs);
3412b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3413b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID: {
3414b5a4add17815167d20a90645779df34cdf45280dFred Quintana                selection = appendAccountToSelection(uri, selection);
3415b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3416b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3417b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
3418b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
3419b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        selectionWithId, selectionArgs);
3420b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
342135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3422d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
3423dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(values, selection, selectionArgs, callerIsSyncAdapter);
342400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
342500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
342600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3427d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
3428dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(ContentUris.parseId(uri), values, callerIsSyncAdapter);
3429c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
3430c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
3431c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
343224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
343324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // Restrict update to the user's profile.
343424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                StringBuilder profileSelection = new StringBuilder();
343524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                profileSelection.append(Contacts.IS_USER_PROFILE + "=1");
343624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                if (!TextUtils.isEmpty(selection)) {
343724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    profileSelection.append(" AND (").append(selection).append(")");
343824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                }
343924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                count = updateContactOptions(values, profileSelection.toString(), selectionArgs,
344024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        callerIsSyncAdapter);
344124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
344224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
344324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
34442e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
34452e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
34462e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
34472e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
34482e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
3449fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
3450fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
34512e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
34522e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
34532e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
3454dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(contactId, values, callerIsSyncAdapter);
34552e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                break;
34562e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
34572e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
34587d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            case RAW_CONTACTS_DATA: {
34597d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                final String rawContactId = uri.getPathSegments().get(1);
34607d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
34617d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                    + (selection == null ? "" : " AND " + selection);
34627d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
34637d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter);
34647d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
34657d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                break;
34667d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            }
34677d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
346820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
3469944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                count = updateData(uri, values, appendAccountToSelection(uri, selection),
3470f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
347181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3472f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
347381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
347420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
347520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3476c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
347748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
347848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
347948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
348048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
3481f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
348281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3483f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
348481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
348500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
348600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
34877e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
34885ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
34895ac5c70d1165309302ebcc931f51723e37d31e0bJeff Sharkey                selection = appendAccountToSelection(uri, selection);
3490dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateRawContacts(values, selection, selectionArgs, callerIsSyncAdapter);
34917e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
34927e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
34937e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
34945ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
349533b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
34964529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                if (selection != null) {
34974da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
34984da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=?"
3499dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                                    + " AND(" + selection + ")", selectionArgs,
3500dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
35014529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                } else {
35024da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(rawContactId);
3503dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    count = updateRawContacts(values, RawContacts._ID + "=?", mSelectionArgs1,
3504dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
35054529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                }
35067e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
35077e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
35087e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
3509ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
35105aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, appendAccountToSelection(uri, selection),
3511f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
351281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3513f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
351481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3515ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3516ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3517ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3518ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3519ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
35204da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(groupId));
35214da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                String selectionWithId = Groups._ID + "=? "
352273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                        + (selection == null ? "" : " AND " + selection);
35235aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, selectionWithId, selectionArgs,
35245aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        callerIsSyncAdapter);
352581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3526f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
352781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3528ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3529ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3530ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3531127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
3532de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                count = updateAggregationException(mDb, values);
3533b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
3534b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
3535b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
3536eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
3537e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                count = updateSettings(uri, values, appendAccountToSelection(uri, selection),
3538e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        selectionArgs);
353943880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3540eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
3541eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3542eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
35439705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            case STATUS_UPDATES: {
35449705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                count = updateStatusUpdate(uri, values, selection, selectionArgs);
35459705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                break;
35469705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            }
35479705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
35483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
35493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItems(uri, values, selection, selectionArgs);
35503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
35513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
35523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
35533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
35543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItems(uri, values, StreamItemsColumns.CONCRETE_ID + "=?",
35553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{uri.getLastPathSegment()});
35563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
35573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
35583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
35593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
35603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values, selection, selectionArgs);
35613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
35623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
35633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
35643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
35653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
35663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values,
35673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotos.STREAM_ITEM_ID + "=?", new String[]{streamItemId});
35683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
35693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
35703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
35713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
35723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
35733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
35743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values,
35753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=? AND " +
35763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                                StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=?",
35773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{streamItemPhotoId, streamItemId});
35783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
35793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
35803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
358172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            case DIRECTORIES: {
3582bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                mContactDirectoryManager.scanPackagesByUid(Binder.getCallingUid());
358372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                count = 1;
3584d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
3585d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
3586d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
358746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            case DATA_USAGE_FEEDBACK_ID: {
358846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (handleDataUsageFeedback(uri)) {
358946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    count = 1;
359046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                } else {
359146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    count = 0;
359246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
359346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                break;
359446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
359546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
359681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
359781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
3598f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
359981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
360000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
360100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
360200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
36034f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
36044f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
36059705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private int updateStatusUpdate(Uri uri, ContentValues values, String selection,
36069705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        String[] selectionArgs) {
36079705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // update status_updates table, if status is provided
36089705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO should account type/name be appended to the where clause?
36099705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        int updateCount = 0;
36109705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContentValues settableValues = getSettableColumnsForStatusUpdatesTable(values);
36119705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
36129705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.STATUS_UPDATES,
36139705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    settableValues,
36149705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    getWhereClauseForStatusUpdatesTable(selection),
36159705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selectionArgs);
36169705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
36179705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
36189705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // now update the Presence table
36199705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        settableValues = getSettableColumnsForPresenceTable(values);
36209705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
36219705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.PRESENCE, settableValues,
36229705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selection, selectionArgs);
36239705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
36249705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO updateCount is not entirely a valid count of updated rows because 2 tables could
36259705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // potentially get updated in this method.
36269705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return updateCount;
36279705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
36289705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
36293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int updateStreamItems(Uri uri, ContentValues values, String selection,
36303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
36313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Stream items can't be moved to a new raw contact.
36323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        values.remove(StreamItems.RAW_CONTACT_ID);
36333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
36343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check that the stream items being updated belong to the account.
36353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
36363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItems(account, selection, selectionArgs);
36373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
36386802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to update accounts params - they don't exist in the stream items table.
36396802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_NAME);
36406802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_TYPE);
36416802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
36423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If there's been no exception, the update should be fine.
36433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return mDb.update(Tables.STREAM_ITEMS, values, selection, selectionArgs);
36443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
36453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
36463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int updateStreamItemPhotos(Uri uri, ContentValues values, String selection,
36473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
36483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Stream item photos can't be moved to a new stream item.
36493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        values.remove(StreamItemPhotos.STREAM_ITEM_ID);
36503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
36513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check that the stream item photos being updated belong to the account.
36523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
36533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItemPhotos(account, selection, selectionArgs);
36543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
36556802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to update accounts params - they don't exist in the stream item
36566802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // photos table.
36576802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_NAME);
36586802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_TYPE);
36596802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
36606802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Process the photo (since we're updating, it's valid for the photo to not be present).
36616802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (processStreamItemPhoto(values, true)) {
36626802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // If there's been no exception, the update should be fine.
36636802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return mDb.update(Tables.STREAM_ITEM_PHOTOS, values, selection, selectionArgs);
36646802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
36656802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        return 0;
36663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
36673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
36689705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    /**
36699705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     * Build a where clause to select the rows to be updated in status_updates table.
36709705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     */
36719705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private String getWhereClauseForStatusUpdatesTable(String selection) {
36729705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.setLength(0);
36739705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE);
36749705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(selection);
36759705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(")");
36769705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mSb.toString();
36779705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
36789705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
36799705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForStatusUpdatesTable(ContentValues values) {
36809705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
36819705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS, values,
36829705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS);
36839705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_TIMESTAMP, values,
36849705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_TIMESTAMP);
36859705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_RES_PACKAGE, values,
36869705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_RES_PACKAGE);
36879705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_LABEL, values,
36889705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_LABEL);
36899705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_ICON, values,
36909705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_ICON);
36919705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
36929705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
36939705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
36949705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForPresenceTable(ContentValues values) {
36959705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
36969705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.PRESENCE, values,
36979705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.PRESENCE);
3698aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.CHAT_CAPABILITY, values,
3699aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                StatusUpdates.CHAT_CAPABILITY);
37009705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
37019705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
37029705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
37035aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int updateGroups(Uri uri, ContentValues values, String selectionWithId,
3704f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
370573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3706ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
3707ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
370873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        ContentValues updatedValues;
3709f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter && !values.containsKey(Groups.DIRTY)) {
371073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = mValues;
371173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.clear();
371273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.putAll(values);
371373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.put(Groups.DIRTY, 1);
371473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } else {
371573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = values;
371673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
371773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3718ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        int count = mDb.update(Tables.GROUPS, updatedValues, selectionWithId, selectionArgs);
37191a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) {
37201a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
372194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
372243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
372343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        // TODO: This will not work for groups that have a data set specified, since the content
372443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        // resolver will not be able to request a sync for the right source (unless it is updated
372543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        // to key off account with data set).
37266ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        if (updatedValues.containsKey(Groups.SHOULD_SYNC)
37271129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                && updatedValues.getAsInteger(Groups.SHOULD_SYNC) != 0) {
37286ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups.ACCOUNT_NAME,
3729e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    Groups.ACCOUNT_TYPE}, selectionWithId, selectionArgs, null,
37306ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    null, null);
37316ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountName;
37326ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountType;
37336ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            try {
37346ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                while (c.moveToNext()) {
37356ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountName = c.getString(0);
37366ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountType = c.getString(1);
373724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
37386ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        Account account = new Account(accountName, accountType);
3739ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        ContentResolver.requestSync(account, ContactsContract.AUTHORITY,
37406ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                                new Bundle());
37416ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        break;
37426ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    }
37436ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                }
37446ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            } finally {
37456ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                c.close();
37466ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            }
37476ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        }
374894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        return count;
374994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
375094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
3751b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private int updateSettings(Uri uri, ContentValues values, String selection,
3752b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            String[] selectionArgs) {
3753e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.update(Tables.SETTINGS, values, selection, selectionArgs);
37541a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
37551a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3756e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
3757e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3758e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3759e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3760dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContacts(ContentValues values, String selection, String[] selectionArgs,
3761dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
37624529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (values.containsKey(RawContacts.CONTACT_ID)) {
37634529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            throw new IllegalArgumentException(RawContacts.CONTACT_ID + " should not be included " +
37644529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    "in content values. Contact IDs are assigned automatically");
37654529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
376673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
376797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
376897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
376997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0");
377097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
377197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
37724529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = 0;
3773ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        Cursor cursor = mDb.query(Views.RAW_CONTACTS,
377451bf5ea9531b9da72caff607dbdf35fd6f61cbe2Jeff Sharkey                new String[] { RawContacts._ID }, selection,
37754529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                selectionArgs, null, null, null);
37764529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        try {
37774529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            while (cursor.moveToNext()) {
37784529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                long rawContactId = cursor.getLong(0);
3779dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContact(rawContactId, values, callerIsSyncAdapter);
37804529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count++;
37814529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            }
37824529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        } finally {
37834529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            cursor.close();
37844529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
37854529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
37864529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return count;
37874529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    }
37884529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
3789dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContact(long rawContactId, ContentValues values,
3790dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
379124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
379224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // Enforce profile permissions if the raw contact is in the user's profile.
3793afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        enforceProfilePermissionForRawContact(mDb, rawContactId, true);
379424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
379596b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        final String selection = RawContacts._ID + " = ?";
379696b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(rawContactId);
379719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final boolean requestUndoDelete = (values.containsKey(RawContacts.DELETED)
379819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                && values.getAsInteger(RawContacts.DELETED) == 0);
379919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int previousDeleted = 0;
3800ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType = null;
3801ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName = null;
380243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String dataSet = null;
380319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        if (requestUndoDelete) {
380419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            Cursor cursor = mDb.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS, selection,
380596b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                    mSelectionArgs1, null, null, null);
380619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            try {
380719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                if (cursor.moveToFirst()) {
380819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    previousDeleted = cursor.getInt(RawContactsQuery.DELETED);
3809ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountType = cursor.getString(RawContactsQuery.ACCOUNT_TYPE);
3810ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountName = cursor.getString(RawContactsQuery.ACCOUNT_NAME);
381143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    dataSet = cursor.getString(RawContactsQuery.DATA_SET);
381219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                }
381319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            } finally {
381419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                cursor.close();
381519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
381619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
381719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT);
381819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        }
3819f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
382096b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        int count = mDb.update(Tables.RAW_CONTACTS, values, selection, mSelectionArgs1);
38215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count != 0) {
3822f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            if (values.containsKey(RawContacts.AGGREGATION_MODE)) {
3823f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                int aggregationMode = values.getAsInteger(RawContacts.AGGREGATION_MODE);
3824f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov
3825f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // As per ContactsContract documentation, changing aggregation mode
3826f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // to DEFAULT should not trigger aggregation
3827f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                if (aggregationMode != RawContacts.AGGREGATION_MODE_DEFAULT) {
382869cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId, aggregationMode, false);
3829f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                }
3830f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            }
3831433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            if (values.containsKey(RawContacts.STARRED)) {
3832dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter) {
3833dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
3834dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            values.getAsLong(RawContacts.STARRED) != 0);
3835dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
38364529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                mContactAggregator.updateStarred(rawContactId);
3837dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
3838dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // if this raw contact is being associated with an account, then update the
3839dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // favorites group membership based on whether or not this contact is starred.
3840dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // If it is starred, add a group membership, if one doesn't already exist
3841dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // otherwise delete any matching group memberships.
3842dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
3843dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    boolean starred = 0 != DatabaseUtils.longForQuery(mDb,
3844dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            SELECTION_STARRED_FROM_RAW_CONTACTS,
3845dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            new String[]{Long.toString(rawContactId)});
3846dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId, starred);
3847dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
3848dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
3849dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3850dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // if this raw contact is being associated with an account, then add a
3851dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // group membership to the group marked as AutoAdd, if any.
3852dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
3853dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                addAutoAddMembership(rawContactId);
3854433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            }
3855dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3856285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (values.containsKey(RawContacts.SOURCE_ID)) {
38572b7a632bba423357ae5641f94da6a2f71afc523bDmitri Plotnikov                mContactAggregator.updateLookupKeyForRawContact(mDb, rawContactId);
3858285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
3859f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            if (values.containsKey(RawContacts.NAME_VERIFIED)) {
3860f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
3861f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // If setting NAME_VERIFIED for this raw contact, reset it for all
3862f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // other raw contacts in the same aggregate
3863f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                if (values.getAsInteger(RawContacts.NAME_VERIFIED) != 0) {
386478fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                    mDbHelper.resetNameVerifiedForOtherRawContacts(rawContactId);
3865f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                }
3866f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                mContactAggregator.updateDisplayNameForRawContact(mDb, rawContactId);
3867f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            }
386819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            if (requestUndoDelete && previousDeleted == 1) {
3869d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                mTransactionContext.rawContactInserted(rawContactId,
387043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        new AccountWithDataSet(accountName, accountType, dataSet));
387119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
38725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
38735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return count;
387433b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
387533b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
3876321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(Uri uri, ContentValues values, String selection,
3877f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
387820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
387920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
388020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
38815ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
388220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
388320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
388420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
388520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
388620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
3887b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
388820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
388920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
389097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
389197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
389297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    Data.IS_READ_ONLY + "=0");
389397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
389497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
3895653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        int count = 0;
389620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3897653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
3898653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // so we don't need to worry about updating data we don't have permission to read.
3899f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // This query will be allowed to return profiles, and we'll do the permission check
3900f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // within the loop.
39016ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro        Cursor c = queryLocal(uri.buildUpon()
3902f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                .appendQueryParameter(ContactsContract.ALLOW_PROFILE, "1").build(),
3903f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                DataRowHandler.DataUpdateQuery.COLUMNS,
39046ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                selection, selectionArgs, null, -1 /* directory ID */,
39056ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                true /* suppress profile check */);
3906653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        try {
3907653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            while(c.moveToNext()) {
390824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // Check profile permission for the raw contact that owns each data record.
390924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = c.getLong(DataRowHandler.DataUpdateQuery.RAW_CONTACT_ID);
3910afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForRawContact(mDb, rawContactId, true);
391124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
3912f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count += updateData(mValues, c, callerIsSyncAdapter);
391320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3914653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        } finally {
3915653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            c.close();
391620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
391720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3918653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return count;
391920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
392020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3921f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
3922653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        if (values.size() == 0) {
3923653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return 0;
3924321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
3925653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
3926f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        final String mimeType = c.getString(DataRowHandler.DataUpdateQuery.MIMETYPE);
3927a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
3928f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        boolean updated =
3929f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                rowHandler.update(mDb, mTransactionContext, values, c, callerIsSyncAdapter);
3930f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
3931f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
3932a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
3933f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        return updated ? 1 : 0;
3934321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    }
3935321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
39368c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(ContentValues values, String selection,
3937dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
39388c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        int count = 0;
3939ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        Cursor cursor = mDb.query(Views.CONTACTS,
394024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                new String[] { Contacts._ID, Contacts.IS_USER_PROFILE }, selection,
39418c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                selectionArgs, null, null, null);
39428c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        try {
39438c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            while (cursor.moveToNext()) {
39448c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                long contactId = cursor.getLong(0);
394524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
394624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // Check for profile write permission before updating a user's profile contact.
394724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                boolean isProfile = cursor.getInt(1) == 1;
394824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                if (isProfile) {
394924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    enforceProfilePermission(true);
395024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                }
395124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
3952dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateContactOptions(contactId, values, callerIsSyncAdapter);
39538c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count++;
39548c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            }
39558c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        } finally {
39568c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            cursor.close();
39578c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        }
39588c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
39598c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return count;
39608c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
39618c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
3962dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateContactOptions(long contactId, ContentValues values,
3963dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
3964d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
396524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // Check write permission if the contact is the user's profile.
3966afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        enforceProfilePermissionForContact(mDb, contactId, true);
396724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
39688c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
3969b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
3970d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
3971b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
3972d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
3973b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
3974d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
3975b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
3976d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
3977b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
3978d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.STARRED);
3979d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3980d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
39818c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.size() == 0) {
3982d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
3983d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
3984d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
39858c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.containsKey(RawContacts.STARRED)) {
3986c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            // Mark dirty when changing starred to trigger sync
39878c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            mValues.put(RawContacts.DIRTY, 1);
3988c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey        }
3989c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey
39904da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(contactId);
399197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        mDb.update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=?"
399297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                + " AND " + RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0", mSelectionArgs1);
39938c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
3994dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (mValues.containsKey(RawContacts.STARRED) && !callerIsSyncAdapter) {
3995ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann            Cursor cursor = mDb.query(Views.RAW_CONTACTS,
3996dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[] { RawContacts._ID }, RawContacts.CONTACT_ID + "=?",
3997dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    mSelectionArgs1, null, null, null);
3998dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            try {
3999dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                while (cursor.moveToNext()) {
4000dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    long rawContactId = cursor.getLong(0);
4001dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
4002dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            mValues.getAsLong(RawContacts.STARRED) != 0);
4003dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
4004dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } finally {
4005dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                cursor.close();
4006dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
4007dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
4008dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
40098c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // Copy changeable values to prevent automatically managed fields from
40108c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // being explicitly updated by clients.
40118c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
4012b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
40138c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
4014b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
40158c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
4016b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
40178c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
4018b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
40198c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
4020b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
40218c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.STARRED);
40228c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
40239b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        int rslt = mDb.update(Tables.CONTACTS, mValues, Contacts._ID + "=?", mSelectionArgs1);
40246e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori
40259b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        if (values.containsKey(Contacts.LAST_TIME_CONTACTED) &&
40269b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori                !values.containsKey(Contacts.TIMES_CONTACTED)) {
40279b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
40289b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
40299b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        }
40309b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        return rslt;
4031f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
4032d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
4033127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
4034127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
40350c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId1 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID1);
40360c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId2 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID2);
403780c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
4038ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId1;
4039ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId2;
40400c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (rcId1 < rcId2) {
40410c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId1;
40420c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId2;
40430c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
40440c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId1;
40450c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId2;
4046b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
4047127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
40480c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
40494da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[0] = String.valueOf(rawContactId1);
40504da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[1] = String.valueOf(rawContactId2);
40510c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.delete(Tables.AGGREGATION_EXCEPTIONS,
40524da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    AggregationExceptions.RAW_CONTACT_ID1 + "=? AND "
40534da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    + AggregationExceptions.RAW_CONTACT_ID2 + "=?", mSelectionArgs2);
40540c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
40556bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            ContentValues exceptionValues = new ContentValues(3);
40566bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
40570c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
40580c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
40590c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
40600c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    exceptionValues);
4061127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
4062127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
40633389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
406469cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId1,
406569cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
406669cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId2,
406769cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
4068dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov
4069bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        mContactAggregator.aggregateContact(mTransactionContext, db, rawContactId1);
4070bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        mContactAggregator.aggregateContact(mTransactionContext, db, rawContactId2);
4071127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
4072127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
4073127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
4074127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
4075b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
4076b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
407770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    public void onAccountsUpdated(Account[] accounts) {
4078bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
40793826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
40803826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
4081bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected boolean updateAccountsInBackground(Account[] accounts) {
4082f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao        // TODO : Check the unit test.
4083e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov        boolean accountsChanged = false;
408449d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov        mDb = mDbHelper.getWritableDatabase();
408570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        mDb.beginTransaction();
408670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        try {
408743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            Set<AccountWithDataSet> existingAccountsWithDataSets =
408843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    findValidAccountsWithDataSets(Tables.ACCOUNTS);
4089743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov
409043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // Add a row to the ACCOUNTS table (with no data set) for each new account.
4091743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            for (Account account : accounts) {
409243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                AccountWithDataSet accountWithDataSet = new AccountWithDataSet(
409343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        account.name, account.type, null);
409443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                if (!existingAccountsWithDataSets.contains(accountWithDataSet)) {
4095e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    accountsChanged = true;
409643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
409743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    // Add an account entry with an empty data set to match the account.
4098743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                    mDb.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME
409943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            + ", " + RawContacts.ACCOUNT_TYPE + ", " + RawContacts.DATA_SET
410043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            + ") VALUES (?, ?, ?)",
410143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            new String[] {
410243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getAccountName(),
410343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getAccountType(),
410443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getDataSet()
410543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            });
4106743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                }
4107743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            }
410848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
410943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // Check each of the existing sub-accounts against the account list.  If the owning
411043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // account no longer exists, the sub-account and all its data should be deleted.
411143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            List<AccountWithDataSet> accountsWithDataSetsToDelete =
411243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    new ArrayList<AccountWithDataSet>();
411343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            List<Account> accountList = Arrays.asList(accounts);
411443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            for (AccountWithDataSet accountWithDataSet : existingAccountsWithDataSets) {
411543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                Account owningAccount = new Account(
411643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        accountWithDataSet.getAccountName(), accountWithDataSet.getAccountType());
411743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                if (!accountList.contains(owningAccount)) {
411843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    accountsWithDataSetsToDelete.add(accountWithDataSet);
411943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                }
412070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
412170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
412243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            if (!accountsWithDataSetsToDelete.isEmpty()) {
4123e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                accountsChanged = true;
412443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                for (AccountWithDataSet accountWithDataSet : accountsWithDataSetsToDelete) {
412543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    Log.d(TAG, "removing data for removed account " + accountWithDataSet);
412643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    String[] accountParams = new String[] {
412743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            accountWithDataSet.getAccountName(),
412843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            accountWithDataSet.getAccountType()
412943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    };
413043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    String[] accountWithDataSetParams = accountWithDataSet.getDataSet() == null
413143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            ? accountParams
413243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            : new String[] {
413343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getAccountName(),
413443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getAccountType(),
413543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getDataSet()
413643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            };
413743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    String groupsDataSetClause = " AND " + Groups.DATA_SET
413843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            + (accountWithDataSet.getDataSet() == null ? " IS NULL" : " = ?");
413943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    String rawContactsDataSetClause = " AND " + RawContacts.DATA_SET
414043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            + (accountWithDataSet.getDataSet() == null ? " IS NULL" : " = ?");
414143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
4142e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4143e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.GROUPS +
4144e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Groups.ACCOUNT_NAME + " = ?" +
414543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    " AND " + Groups.ACCOUNT_TYPE + " = ?" +
414643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    groupsDataSetClause, accountWithDataSetParams);
4147e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4148e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.PRESENCE +
4149e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (" +
4150e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    "SELECT " + RawContacts._ID +
4151e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " FROM " + Tables.RAW_CONTACTS +
4152e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
415343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    " AND " + RawContacts.ACCOUNT_TYPE + " = ?" +
415443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    rawContactsDataSetClause + ")", accountWithDataSetParams);
4155e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4156e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.RAW_CONTACTS +
4157e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
415843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            " AND " + RawContacts.ACCOUNT_TYPE + " = ?" +
415943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            rawContactsDataSetClause, accountWithDataSetParams);
4160e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4161e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.SETTINGS +
4162e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Settings.ACCOUNT_NAME + " = ?" +
416343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            " AND " + Settings.ACCOUNT_TYPE + " = ?", accountParams);
4164e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4165e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.ACCOUNTS +
4166e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + "=?" +
416743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            " AND " + RawContacts.ACCOUNT_TYPE + "=?" +
416843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            rawContactsDataSetClause, accountWithDataSetParams);
4169d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    mDb.execSQL(
4170d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            "DELETE FROM " + Tables.DIRECTORIES +
4171d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            " WHERE " + Directory.ACCOUNT_NAME + "=?" +
417243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            " AND " + Directory.ACCOUNT_TYPE + "=?", accountParams);
41734458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    resetDirectoryCache();
4174e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                }
4175e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov
417633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // Find all aggregated contacts that used to contain the raw contacts
417733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // we have just deleted and see if they are still referencing the deleted
4178e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                // names or photos.  If so, fix up those contacts.
417933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                HashSet<Long> orphanContactIds = Sets.newHashSet();
418033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                Cursor cursor = mDb.rawQuery("SELECT " + Contacts._ID +
418133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " FROM " + Tables.CONTACTS +
418233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " WHERE (" + Contacts.NAME_RAW_CONTACT_ID + " NOT NULL AND " +
418369cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                Contacts.NAME_RAW_CONTACT_ID + " NOT IN " +
418469cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + RawContacts._ID +
418569cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.RAW_CONTACTS + "))" +
418633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " OR (" + Contacts.PHOTO_ID + " NOT NULL AND " +
418733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                                Contacts.PHOTO_ID + " NOT IN " +
418869cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + Data._ID +
418969cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.DATA + "))", null);
419033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                try {
419133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    while (cursor.moveToNext()) {
419233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        orphanContactIds.add(cursor.getLong(0));
419333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    }
419433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                } finally {
419533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    cursor.close();
419633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
419733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
419833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                for (Long contactId : orphanContactIds) {
4199bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov                    mContactAggregator.updateAggregateData(mTransactionContext, contactId);
420033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
4201e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                mDbHelper.updateAllVisible();
4202bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov                updateSearchIndexInTransaction();
420333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            }
420433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
420543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // Now that we've done the account-based additions and subtractions from the Accounts
420643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // table, check for raw contacts that have been added with a data set and add Accounts
420743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // entries for those if necessary.
420843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            existingAccountsWithDataSets = findValidAccountsWithDataSets(Tables.ACCOUNTS);
420943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            Set<AccountWithDataSet> rawContactAccountsWithDataSets =
421043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    findValidAccountsWithDataSets(Tables.RAW_CONTACTS);
421143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            rawContactAccountsWithDataSets.removeAll(existingAccountsWithDataSets);
421243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
421343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // Any remaining raw contact sub-accounts need to be added to the Accounts table.
421443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            for (AccountWithDataSet accountWithDataSet : rawContactAccountsWithDataSets) {
421543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                accountsChanged = true;
421643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
421743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                // Add an account entry to match the raw contact.
421843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                mDb.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME
421943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + ", " + RawContacts.ACCOUNT_TYPE + ", " + RawContacts.DATA_SET
422043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + ") VALUES (?, ?, ?)",
422143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        new String[] {
422243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                accountWithDataSet.getAccountName(),
422343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                accountWithDataSet.getAccountType(),
422443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                accountWithDataSet.getDataSet()
422543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        });
422643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
422743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
4228e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            if (accountsChanged) {
422943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                // TODO: Should sync state take data set into consideration?
4230e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                mDbHelper.getSyncState().onAccountsChanged(mDb, accounts);
4231e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            }
423270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.setTransactionSuccessful();
423370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } finally {
423470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.endTransaction();
423570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
423673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.clear();
42373826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
42383826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (accountsChanged) {
42393826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateContactsAccountCount(accounts);
42403826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
42413826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
42423826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
4243afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov        return accountsChanged;
424470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
4245619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
42463826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateContactsAccountCount(Account[] accounts) {
42473826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        int count = 0;
42483826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        for (Account account : accounts) {
42493826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            if (isContactsAccount(account)) {
42503826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                count++;
42513826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            }
42523826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
42533826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mContactsAccountCount = count;
42543826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
42553826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
42563826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    protected boolean isContactsAccount(Account account) {
42573826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        final IContentService cs = ContentResolver.getContentService();
42583826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        try {
42593826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return cs.getIsSyncable(account, ContactsContract.AUTHORITY) > 0;
42603826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } catch (RemoteException e) {
42613826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            Log.e(TAG, "Cannot obtain sync flag for account: " + account, e);
42623826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return false;
42633826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
42643826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
42653826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
426672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void onPackageChanged(String packageName) {
4267bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_DIRECTORIES, packageName);
4268d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4269d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4270619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
427143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * Finds all distinct account types and data sets present in the specified table.
4272627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     */
427343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    private Set<AccountWithDataSet> findValidAccountsWithDataSets(String table) {
427443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        Set<AccountWithDataSet> accountsWithDataSets = new HashSet<AccountWithDataSet>();
4275743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov        Cursor c = mDb.rawQuery(
427643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                "SELECT DISTINCT " + RawContacts.ACCOUNT_NAME + "," + RawContacts.ACCOUNT_TYPE +
427743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                "," + RawContacts.DATA_SET +
427843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                " FROM " + table, null);
4279627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
4280627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            while (c.moveToNext()) {
4281dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!c.isNull(0) || !c.isNull(1)) {
428243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    accountsWithDataSets.add(
428343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            new AccountWithDataSet(c.getString(0), c.getString(1), c.getString(2)));
4284627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
4285627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
4286627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } finally {
4287627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            c.close();
4288627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
428943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        return accountsWithDataSets;
4290627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
4291627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
42924f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
42934f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
42944f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
429515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
429615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mReadAccessLatch);
429715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
4298d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String directory = getQueryParameter(uri, ContactsContract.DIRECTORY_PARAM_KEY);
4299385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directory == null) {
43003716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            return wrapCursor(uri,
43016ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder, -1, false));
4302385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directory.equals("0")) {
43033716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            return wrapCursor(uri,
43043716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder,
43056ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                            Directory.DEFAULT, false));
4306d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        } else if (directory.equals("1")) {
43073716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            return wrapCursor(uri,
43083716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder,
43096ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                            Directory.LOCAL_INVISIBLE, false));
4310d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4311d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4312d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        DirectoryInfo directoryInfo = getDirectoryAuthority(directory);
4313d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo == null) {
4314a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            Log.e(TAG, "Invalid directory ID: " + uri);
4315a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            return null;
4316d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4317d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4318d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Builder builder = new Uri.Builder();
4319d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.scheme(ContentResolver.SCHEME_CONTENT);
4320d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.authority(directoryInfo.authority);
4321d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.encodedPath(uri.getEncodedPath());
4322d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountName != null) {
4323d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_NAME, directoryInfo.accountName);
4324d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4325d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountType != null) {
4326d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, directoryInfo.accountType);
4327d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
43282e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
43292e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limit = getLimit(uri);
43302e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        if (limit != null) {
43312e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov            builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, limit);
43322e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        }
43332e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
4334d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Uri directoryUri = builder.build();
433509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
433609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        if (projection == null) {
433709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            projection = getDefaultProjection(uri);
433809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
433909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
4340332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        Cursor cursor = getContext().getContentResolver().query(directoryUri, projection, selection,
4341d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                selectionArgs, sortOrder);
43426ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
43436ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (cursor == null) {
43446ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return null;
43456ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
43466ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
4347547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        CrossProcessCursor crossProcessCursor = getCrossProcessCursor(cursor);
4348547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        if (crossProcessCursor != null) {
4349547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro            return wrapCursor(uri, cursor);
4350547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        } else {
4351547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro            return matrixCursorFromCursor(wrapCursor(uri, cursor));
4352547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        }
43533716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    }
43543716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
4355547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro    private Cursor wrapCursor(Uri uri, Cursor cursor) {
4356547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro
4357547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        // If the cursor doesn't contain a snippet column, don't bother wrapping it.
4358547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        if (cursor.getColumnIndex(SearchSnippetColumns.SNIPPET) < 0) {
4359547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro            return cursor;
4360547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        }
4361547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro
43623716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // Parse out snippet arguments for use when snippets are retrieved from the cursor.
43633716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String[] args = null;
43643716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String snippetArgs =
43653716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                getQueryParameter(uri, SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY);
43663716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        if (snippetArgs != null) {
43673716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            args = snippetArgs.split(",");
43683716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        }
43693716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
43703716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String query = uri.getLastPathSegment();
43713716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String startMatch = args != null && args.length > 0 ? args[0]
43723716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_START_MATCH;
43733716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String endMatch = args != null && args.length > 1 ? args[1]
43743716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_END_MATCH;
43753716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String ellipsis = args != null && args.length > 2 ? args[2]
43763716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_ELLIPSIS;
43773716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        int maxTokens = args != null && args.length > 3 ? Integer.parseInt(args[3])
43783716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_MAX_TOKENS;
43793716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
4380547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        return new SnippetizingCursorWrapper(cursor, query, startMatch, endMatch, ellipsis,
4381547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro                maxTokens);
43826ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
43836ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
43846ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    private CrossProcessCursor getCrossProcessCursor(Cursor cursor) {
43856ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        Cursor c = cursor;
43866ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (c instanceof CrossProcessCursor) {
43876ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return (CrossProcessCursor) c;
43886ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else if (c instanceof CursorWindow) {
43896ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return getCrossProcessCursor(((CursorWrapper) c).getWrappedCursor());
43906ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else {
43916ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return null;
43926ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
43936ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
43946ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
43956ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    public MatrixCursor matrixCursorFromCursor(Cursor cursor) {
43966ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        MatrixCursor newCursor = new MatrixCursor(cursor.getColumnNames());
43976ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        int numColumns = cursor.getColumnCount();
43986ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        String data[] = new String[numColumns];
43996ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        cursor.moveToPosition(-1);
44006ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        while (cursor.moveToNext()) {
44016ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            for (int i = 0; i < numColumns; i++) {
44026ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov                data[i] = cursor.getString(i);
44036ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            }
44046ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            newCursor.addRow(data);
4405332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        }
44066ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        return newCursor;
4407d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4408d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4409d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final class DirectoryQuery {
4410d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
4411d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory._ID,
4412d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.DIRECTORY_AUTHORITY,
4413d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_NAME,
4414d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_TYPE
4415d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        };
4416d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4417d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int DIRECTORY_ID = 0;
4418d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int AUTHORITY = 1;
4419d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_NAME = 2;
4420d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_TYPE = 3;
4421d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4422d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4423d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
4424d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Reads and caches directory information for the database.
4425d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
4426d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private DirectoryInfo getDirectoryAuthority(String directoryId) {
44274458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized (mDirectoryCache) {
44284458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            if (!mDirectoryCacheValid) {
44294458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCache.clear();
443049d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                SQLiteDatabase db = mDbHelper.getReadableDatabase();
443149d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                Cursor cursor = db.query(Tables.DIRECTORIES,
44324458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryQuery.COLUMNS,
44334458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        null, null, null, null, null);
44344458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                try {
44354458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    while (cursor.moveToNext()) {
44364458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryInfo info = new DirectoryInfo();
44374458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        String id = cursor.getString(DirectoryQuery.DIRECTORY_ID);
44384458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.authority = cursor.getString(DirectoryQuery.AUTHORITY);
44394458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
44404458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
44414458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        mDirectoryCache.put(id, info);
44424458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    }
44434458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                } finally {
44444458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    cursor.close();
4445d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                }
44464458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCacheValid = true;
4447d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4448d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
44494458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            return mDirectoryCache.get(directoryId);
44504458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
4451d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4452d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
445372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void resetDirectoryCache() {
44544458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized(mDirectoryCache) {
44554458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            mDirectoryCacheValid = false;
44564458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
445772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
445872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
44596ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro    private Cursor queryLocal(Uri uri, String[] projection, String selection,
44606ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro            String[] selectionArgs, String sortOrder, long directoryId,
44616ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro            boolean suppressProfileCheck) {
4462bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
4463bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            Log.v(TAG, "query: " + uri);
4464bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        }
44650b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov
4466b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
446735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4468d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
44691f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
4470c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limit = getLimit(uri);
4471c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4472a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
44734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
447435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
4475b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().query(db, projection, selection,  selectionArgs,
447635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                        sortOrder);
447735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4478d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
4479763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
448024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                boolean existingWhere = appendLocalDirectorySelectionIfNeeded(qb, directoryId);
44816ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                appendProfileRestriction(qb, uri, Contacts.IS_USER_PROFILE, existingWhere,
44826ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                        suppressProfileCheck);
44836ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                sortOrder = prependProfileSortIfNeeded(uri, sortOrder, suppressProfileCheck);
4484619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
4485619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
4486619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
4487d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
44884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
4489afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
4490763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
44914da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
44924da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
44936bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
44946bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
44956bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
44965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP:
44975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
44985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
44995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int segmentCount = pathSegments.size();
45005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount < 3) {
4501fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
4502fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
45035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
4504a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
45055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String lookupKey = pathSegments.get(2);
45065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount == 4) {
45075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
4508afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                    enforceProfilePermissionForContact(db, contactId, false);
45095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
4510763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
4511a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4512a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
4513a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4514a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
4515a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
45165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        return c;
45175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
45185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
45195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
4520763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
45214da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
45224da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
45234da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
45245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                break;
45255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
45265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
45272149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_DATA:
45282149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_DATA: {
45292149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
45302149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                int segmentCount = pathSegments.size();
45312149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount < 4) {
45322149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
45332149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                            "Missing a lookup key", uri));
45342149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
45352149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
45362149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount == 5) {
45372149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
4538afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                    enforceProfilePermissionForContact(db, contactId, false);
45392149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
45402149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    setTablesAndProjectionMapForData(lookupQb, uri, projection, false);
4541a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
4542a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
4543a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4544a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Data.CONTACT_ID, contactId, Data.LOOKUP_KEY, lookupKey);
4545a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
45462149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                        return c;
45472149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    }
45482149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
45492149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    // TODO see if the contact exists but has no data rows (rare)
45502149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
45512149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
45522149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
455324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long contactId = lookupContactIdByLookupKey(db, lookupKey);
4554afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
45552149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
455624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        String.valueOf(contactId));
45572149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                qb.appendWhere(" AND " + Data.CONTACT_ID + "=?");
45582149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                break;
45592149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            }
45602149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
45613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_ID_STREAM_ITEMS: {
45623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                long contactId = Long.parseLong(uri.getPathSegments().get(1));
4563afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
45643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
45653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
45663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(RawContactsColumns.CONCRETE_CONTACT_ID + "=?");
45673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
45683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
45693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
45703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_LOOKUP_STREAM_ITEMS:
45713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_LOOKUP_ID_STREAM_ITEMS: {
45723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                List<String> pathSegments = uri.getPathSegments();
45733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                int segmentCount = pathSegments.size();
45743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                if (segmentCount < 4) {
45753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
45763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                            "Missing a lookup key", uri));
45773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
45783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String lookupKey = pathSegments.get(2);
45793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                if (segmentCount == 5) {
45803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    long contactId = Long.parseLong(pathSegments.get(3));
4581afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                    enforceProfilePermissionForContact(db, contactId, false);
45823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
45833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    setTablesAndProjectionMapForStreamItems(lookupQb);
45843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
45853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
45863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                            RawContacts.CONTACT_ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
45873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    if (c != null) {
45883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        return c;
45893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    }
45903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
45913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
45923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
45933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                long contactId = lookupContactIdByLookupKey(db, lookupKey);
4594afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
45953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
45963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(RawContacts.CONTACT_ID + "=?");
45973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
45983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
45993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
4600f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
460142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
460224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long contactId = lookupContactIdByLookupKey(db, lookupKey);
4603afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
4604ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
4605f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                qb.setProjectionMap(sContactsVCardProjectionMap);
46064da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
460724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        String.valueOf(contactId));
46084da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
4609f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                break;
4610f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            }
4611f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
461242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
461342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
461442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                String currentDateString = dateFormat.format(new Date()).toString();
461542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                return db.rawQuery(
461642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    "SELECT" +
461742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " 'vcards_' || ? || '.vcf' AS " + OpenableColumns.DISPLAY_NAME + "," +
461842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " NULL AS " + OpenableColumns.SIZE,
461942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    new String[] { currentDateString });
462042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
462142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
4622ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_FILTER: {
4623916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                String filterParam = "";
4624ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
4625916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    filterParam = uri.getLastPathSegment();
4626ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
46277ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov                setTablesAndProjectionMapForContactsWithSnippet(
46287ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov                        qb, uri, projection, filterParam, directoryId);
46296ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                appendProfileRestriction(qb, uri, Contacts.IS_USER_PROFILE, false,
46306ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                        suppressProfileCheck);
46316ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                sortOrder = prependProfileSortIfNeeded(uri, sortOrder, suppressProfileCheck);
4632ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4633ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4634ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
4635ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT_FILTER:
4636ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT: {
46372f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Basically the resultant SQL should look like this:
46382f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // (SQL for listing starred items)
46392f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // UNION ALL
46402f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // (SQL for listing frequently contacted items)
46412f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // ORDER BY ...
46422f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
46432f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final boolean phoneOnly = readBooleanQueryParameter(
46442f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        uri, ContactsContract.STREQUENT_PHONE_ONLY, false);
46452f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (match == CONTACTS_STREQUENT_FILTER && uri.getPathSegments().size() > 3) {
46464a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
46474a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
4648e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
46495e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
46502f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    selection = DbQueryUtils.concatenateClauses(selection, sb.toString());
46514a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
46524a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
46532f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                String[] subProjection = null;
46545e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                if (projection != null) {
46552f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    subProjection = appendProjectionArg(projection, TIMES_USED_SORT_COLUMN);
46565e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                }
46575e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
46584a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                // Build the first query for starred
46594928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                setTablesAndProjectionMapForContacts(qb, uri, projection, false);
46604928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                qb.setProjectionMap(phoneOnly ?
46614928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                        sStrequentPhoneOnlyStarredProjectionMap
46624928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                        : sStrequentStarredProjectionMap);
46632f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                qb.appendWhere(DbQueryUtils.concatenateClauses(
46642f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        selection, Contacts.IS_USER_PROFILE + "=0"));
46652f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                qb.setStrict(true);
46662f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final String starredQuery = qb.buildQuery(subProjection,
466724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        Contacts.STARRED + "=1", Contacts._ID, null, null, null);
4668d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
46692f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Reset the builder.
4670d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
46712f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                qb.setStrict(true);
46724928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
46734928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                // Build the second query for frequent part.
46744928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                final String frequentQuery;
46754928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                if (phoneOnly) {
46764928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    final StringBuilder tableBuilder = new StringBuilder();
46774928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // In phone only mode, we need to look at view_data instead of
46784928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // contacts/raw_contacts to obtain actual phone numbers. One problem is that
46794928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // view_data is much larger than view_contacts, so our query might become much
46804928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // slower.
46814928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    //
46824928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // To avoid the possible slow down, we start from data usage table and join
46834928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // view_data to the table, assuming data usage table is quite smaller than
46844928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // data rows (almost always it should be), and we don't want any phone
46854928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // numbers not used by the user. This way sqlite is able to drop a number of
46864928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // rows in view_data in the early stage of data lookup.
46874928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    tableBuilder.append(Tables.DATA_USAGE_STAT
46884928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + " INNER JOIN " + Views.DATA + " " + Tables.DATA
46894928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "="
46904928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                                + DataColumns.CONCRETE_ID + " AND "
46914928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "="
46924928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                                + DataUsageStatColumns.USAGE_TYPE_INT_CALL + ")");
46934928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    appendContactPresenceJoin(tableBuilder, projection, RawContacts.CONTACT_ID);
46944928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    appendContactStatusUpdateJoin(tableBuilder, projection,
46954928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            ContactsColumns.LAST_STATUS_UPDATE_ID);
46964928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
46974928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setTables(tableBuilder.toString());
46984928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setProjectionMap(sStrequentPhoneOnlyFrequentProjectionMap);
46994928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.appendWhere(DbQueryUtils.concatenateClauses(
47004928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            selection,
47014928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            Contacts.STARRED + "=0 OR " + Contacts.STARRED + " IS NULL",
47024928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            MimetypesColumns.MIMETYPE + " IN ("
47034928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + "'" + Phone.CONTENT_ITEM_TYPE + "', "
47044928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + "'" + SipAddress.CONTENT_ITEM_TYPE + "')"));
47054928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    frequentQuery = qb.buildQuery(subProjection, null, null, null, null, null);
47064928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                } else {
47074928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    setTablesAndProjectionMapForContacts(qb, uri, projection, true);
47084928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setProjectionMap(sStrequentFrequentProjectionMap);
47094928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.appendWhere(DbQueryUtils.concatenateClauses(
47104928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            selection,
47114928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            "(" + Contacts.STARRED + " =0 OR " + Contacts.STARRED + " IS NULL)",
47124928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            Contacts.IS_USER_PROFILE + "=0"));
47134928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    frequentQuery = qb.buildQuery(subProjection,
47144928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            null, Contacts._ID, null, null, null);
47154928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                }
4716d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
4717d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
47182f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final String unionQuery =
47192f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
47202f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                                STREQUENT_ORDER_BY, STREQUENT_LIMIT);
47212f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
47222f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Here, we need to use selection / selectionArgs (supplied from users) "twice",
47232f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // as we want them both for starred items and for frequently contacted items.
47242f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                //
47252f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // e.g. if the user specify selection = "starred =?" and selectionArgs = "0",
47262f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // the resultant SQL should be like:
47272f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // SELECT ... WHERE starred =? AND ...
47282f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // UNION ALL
47292f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // SELECT ... WHERE starred =? AND ...
47302f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                String[] doubledSelectionArgs = null;
47312f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (selectionArgs != null) {
47322f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    final int length = selectionArgs.length;
47332f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    doubledSelectionArgs = new String[length * 2];
47347d7d0e95636344c01eb4e4d034791c199bee98e9Daisuke Miyakawa                    System.arraycopy(selectionArgs, 0, doubledSelectionArgs, 0, length);
47357d7d0e95636344c01eb4e4d034791c199bee98e9Daisuke Miyakawa                    System.arraycopy(selectionArgs, 0, doubledSelectionArgs, length, length);
47362f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                }
47372f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
47382f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                Cursor cursor = db.rawQuery(unionQuery, doubledSelectionArgs);
47392f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (cursor != null) {
47402f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    cursor.setNotificationUri(getContext().getContentResolver(),
4741d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
4742d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
47432f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                return cursor;
4744d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
4745d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
474645ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa            case CONTACTS_FREQUENT: {
474745ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                setTablesAndProjectionMapForContacts(qb, uri, projection, true);
474845ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                qb.setProjectionMap(sStrequentFrequentProjectionMap);
4749363bdaba2994539e1a3a2342a9fcf223604d69eaDaisuke Miyakawa                qb.appendWhere(Contacts.IS_USER_PROFILE + "=0");
475045ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                groupBy = Contacts._ID;
475145ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                if (!TextUtils.isEmpty(sortOrder)) {
475245ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                    sortOrder = FREQUENT_ORDER_BY + ", " + sortOrder;
475345ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                } else {
475445ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                    sortOrder = FREQUENT_ORDER_BY;
475545ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                }
475645ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                break;
475745ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa            }
475845ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa
4759ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_GROUP: {
4760763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
4761b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
476271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
47634a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
4764b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
4765b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
4766b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
4767b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
476824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
476924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
477024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForContacts(qb, uri, projection);
477124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(Contacts.IS_USER_PROFILE + "=1");
477224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
477324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
477424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
477524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_ENTITIES: {
477624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
477724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForEntities(qb, uri, projection);
477824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + Contacts.IS_USER_PROFILE + "=1");
477924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
478024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
478124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
478224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA: {
478324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
478424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
478524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1");
478624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
478724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
478824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
478924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA_ID: {
479024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
479124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
479224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
479324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + Data._ID + "=? AND "
479424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1");
479524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
479624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
479724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
479824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD: {
479924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
4800ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
480124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.setProjectionMap(sContactsVCardProjectionMap);
480224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(Contacts.IS_USER_PROFILE + "=1");
480324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
480424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
480524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
4806a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_DATA: {
48074a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
480882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
48094da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
48104da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
48116bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
48126bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
481300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
4814a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
48153653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
4816afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
481782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
48184da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
48194da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
48203653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
48213653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                break;
48223653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
48233653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
4824a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_ENTITIES: {
4825a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
4826a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
4827a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
4828a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
4829a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
4830a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
4831a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4832a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ENTITIES:
4833a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_ENTITIES: {
4834a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
4835a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                int segmentCount = pathSegments.size();
4836a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount < 4) {
4837a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
4838a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            "Missing a lookup key", uri));
4839a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
4840a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
4841a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount == 5) {
4842a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
4843a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
4844a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    setTablesAndProjectionMapForEntities(lookupQb, uri, projection);
4845a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
4846a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4847a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
4848a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4849a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.CONTACT_ID, contactId,
4850a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.LOOKUP_KEY, lookupKey);
4851a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
4852a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        return c;
4853a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    }
4854a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
4855a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4856a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
4857a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
4858a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
4859a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + Contacts.Entity.CONTACT_ID + "=?");
4860a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
4861a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
4862a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
48633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
48643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
48653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
48663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
48673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
48683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
48693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
48703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
48713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItemsColumns.CONCRETE_ID + "=?");
48723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
48733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
48743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
48753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_LIMIT: {
48766802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                MatrixCursor cursor = new MatrixCursor(new String[]{StreamItems.MAX_ITEMS}, 1);
48776802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                cursor.addRow(new Object[]{MAX_STREAM_ITEMS_PER_RAW_CONTACT});
48783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return cursor;
48793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
48803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
48813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
48823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
48833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
48843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
48853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
48863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
48873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
48883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
48893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemId);
48903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=?");
48913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
48923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
48933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
48943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
48953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
48963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
48973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
48983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemPhotoId);
48993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemId);
49003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=? AND " +
49013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=?");
49023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
49033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
49043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
4905f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case PHOTO_DIMENSIONS: {
4906f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                MatrixCursor cursor = new MatrixCursor(
4907f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{DisplayPhoto.DISPLAY_MAX_DIM, DisplayPhoto.THUMBNAIL_MAX_DIM},
4908f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        1);
4909f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                cursor.addRow(new Object[]{mMaxDisplayPhotoDim, mMaxThumbnailPhotoDim});
4910f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                return cursor;
4911f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
4912f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
49134a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case PHONES: {
491482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
491589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
49162815f58f72f109790585931f601a63ddc02536a5Evan Millar                break;
49172815f58f72f109790585931f601a63ddc02536a5Evan Millar            }
49182815f58f72f109790585931f601a63ddc02536a5Evan Millar
491948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID: {
492082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
49214da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
492248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
49234da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
492448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
492548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
492648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
4927ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
492846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
492946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                Integer typeInt = sDataUsageTypeMap.get(typeParam);
493046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (typeInt == null) {
493146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    typeInt = DataUsageStatColumns.USAGE_TYPE_INT_CALL;
493246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
493346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
493489c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
4935ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
49364a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
49374a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
4938a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    sb.append(" AND (");
49395e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
494045d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    boolean hasCondition = false;
49415e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    boolean orNeeded = false;
49425e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String normalizedName = NameNormalizer.normalize(filterParam);
49435e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (normalizedName.length() > 0) {
4944155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN " +
4945155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
4946155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.SEARCH_INDEX +
4947155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " JOIN " + Tables.RAW_CONTACTS +
4948155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
4949155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
4950155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE " + SearchIndexColumns.NAME + " MATCH ");
49512352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                        DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filterParam) + "*");
4952155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(")");
49535e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        orNeeded = true;
495445d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
49555e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
49565e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
4957892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    String number = PhoneNumberUtils.normalizeNumber(filterParam);
4958892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (!TextUtils.isEmpty(number)) {
49595e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        if (orNeeded) {
49605e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                            sb.append(" OR ");
49615e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        }
49625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data._ID +
4963892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                " IN (SELECT DISTINCT " + PhoneLookupColumns.DATA_ID
4964892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " FROM " + Tables.PHONE_LOOKUP
4965892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
4966892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append(number);
4967892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append("%')");
496845d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
496945d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    }
497045d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov
497145d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    if (!hasCondition) {
497245d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // If it is neither a phone number nor a name, the query should return
497345d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // an empty cursor.  Let's ensure that.
497445d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        sb.append("0");
49755e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
49765e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
4977a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
4978ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
49795e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = PhoneColumns.NORMALIZED_NUMBER + "," + RawContacts.CONTACT_ID;
4980a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
498146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
498246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
498346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = accountPromotionSortOrder + ", " + PHONE_FILTER_SORT_ORDER;
498446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    } else {
498546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = PHONE_FILTER_SORT_ORDER;
498646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    }
4987a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
4988ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4989ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4990ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
49914a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case EMAILS: {
499282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
499389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
49944a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                break;
49954a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
49964a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
499748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID: {
499882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
49994da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
50004da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'"
50014da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        + " AND " + Data._ID + "=?");
500248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
500348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
500448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
50055e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_LOOKUP: {
500682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
500789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
50084a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
500908768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String email = uri.getLastPathSegment();
501008768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String address = mDbHelper.extractAddressFromEmailAddress(email);
501108768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, address);
501208768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    qb.appendWhere(" AND UPPER(" + Email.DATA + ")=UPPER(?)");
50134a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
5014ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
5015ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
5016ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
50175e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_FILTER: {
501846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
501946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                Integer typeInt = sDataUsageTypeMap.get(typeParam);
502046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (typeInt == null) {
502146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    typeInt = DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT;
502246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
502346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
502407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                String filterParam = null;
50257d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa
502607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
502707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    filterParam = uri.getLastPathSegment();
502807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    if (TextUtils.isEmpty(filterParam)) {
502907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                        filterParam = null;
503007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    }
503107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                }
50325e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
503307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (filterParam == null) {
503407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    // If the filter is unspecified, return nothing
503507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    qb.appendWhere(" AND 0");
503607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                } else {
503707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    StringBuilder sb = new StringBuilder();
503807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(" AND " + Data._ID + " IN (");
503907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(
504007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            "SELECT " + Data._ID +
504107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " FROM " + Tables.DATA +
50422a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                            " WHERE " + DataColumns.MIMETYPE_ID + "=");
50432a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                    sb.append(mDbHelper.getMimeTypeIdForEmail());
50442a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                    sb.append(" AND " + Data.DATA1 + " LIKE ");
504507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(sb, filterParam + '%');
504620938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                    if (!filterParam.contains("@")) {
5047155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(
5048155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " UNION SELECT " + Data._ID +
5049155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.DATA +
5050155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE +" + DataColumns.MIMETYPE_ID + "=");
5051155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(mDbHelper.getMimeTypeIdForEmail());
5052155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(" AND " + Data.RAW_CONTACT_ID + " IN " +
5053155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
5054155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.SEARCH_INDEX +
5055155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " JOIN " + Tables.RAW_CONTACTS +
5056155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
5057155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
5058155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE " + SearchIndexColumns.NAME + " MATCH ");
50592352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                        DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filterParam) + "*");
5060155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(")");
50615e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
50625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
5063a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
50645e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                }
50655e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
5066a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
506746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
506846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
506946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = accountPromotionSortOrder + ", " + EMAIL_FILTER_SORT_ORDER;
50707d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                    } else {
50717d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                        sortOrder = EMAIL_FILTER_SORT_ORDER;
50727d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                    }
5073a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
50745e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                break;
50755e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
50765e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
5077ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
507882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
507989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
508089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
5081ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
5082ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
5083ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
508448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
508582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
50864da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
508748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
508848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
50894da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
509048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
509148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
509248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
50935ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
5094763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
50956ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                appendProfileRestriction(qb, uri, RawContacts.RAW_CONTACT_IS_USER_PROFILE, true,
50966ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                        suppressProfileCheck);
50974f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
50984f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
50994f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
51005ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
51015ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
5102afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForRawContact(db, rawContactId, false);
5103763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
51044da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
51054da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
51064f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
51074f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
51084f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
51095ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
51105ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
511182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
51124da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
51134da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=?");
51146ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                appendProfileRestriction(qb, uri, RawContacts.RAW_CONTACT_IS_USER_PROFILE, true,
51156ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                        suppressProfileCheck);
511624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
511724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
511824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
51193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case RAW_CONTACTS_ID_STREAM_ITEMS: {
51203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
5121afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForRawContact(db, rawContactId, false);
51223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
51233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
51243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItems.RAW_CONTACT_ID + "=?");
51253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
51263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
512724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
512824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS: {
512924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
513024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForRawContacts(qb, uri);
513124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1");
513224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
513324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
513424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
513524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID: {
513624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
513724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = ContentUris.parseId(uri);
513824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
513924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForRawContacts(qb, uri);
514024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1 AND "
514124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        + RawContacts._ID + "=?");
514224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
514324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
514424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
514524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID_DATA: {
514624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
514724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(2));
514824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
514924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
515024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1 AND "
515124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        + Data.RAW_CONTACT_ID + "=?");
515224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
515324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
515424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
515524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID_ENTITIES: {
515624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
515724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(2));
515824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
515924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForRawEntities(qb, uri);
516024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1 AND "
516124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        + RawContacts._ID + "=?");
5162e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
5163e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
5164e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
5165e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            case DATA: {
516682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
51676ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                appendProfileRestriction(qb, uri, RawContacts.RAW_CONTACT_IS_USER_PROFILE, true,
51686ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                        suppressProfileCheck);
5169e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
5170e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
5171e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
51724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case DATA_ID: {
517324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long dataId = ContentUris.parseId(uri);
5174afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForData(db, dataId, false);
517582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
51764da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
51774da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
5178a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                break;
5179a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            }
5180a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov
5181a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
51824a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
5183a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
5184a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
5185a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
5186892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    sortOrder = " length(lookup.normalized_number) DESC";
5187a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
5188a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
5189e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
5190e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                String numberE164 = PhoneNumberUtils.formatNumberToE164(number,
5191e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                        mDbHelper.getCurrentCountryIso());
5192892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                String normalizedNumber =
5193892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        PhoneNumberUtils.normalizeNumber(number);
5194892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                mDbHelper.buildPhoneLookupAndContactQuery(qb, normalizedNumber, numberE164);
5195e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                qb.setProjectionMap(sPhoneLookupProjectionMap);
5196e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                // Phone lookup cannot be combined with a selection
5197e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selection = null;
5198e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selectionArgs = null;
5199a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
5200a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
5201a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
5202ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
5203ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.GROUPS);
5204ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
520543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                appendAccountFromParameter(qb, uri, true);
5206ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5207ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5208ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5209ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
5210ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.GROUPS);
5211ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
52124da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
52134da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Groups._ID + "=?");
5214ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5215ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5216ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5217ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
5218f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                final boolean returnGroupCountPerAccount =
5219f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        readBooleanQueryParameter(uri, Groups.PARAM_RETURN_GROUP_COUNT_PER_ACCOUNT,
5220f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                                false);
5221f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                qb.setTables(Views.GROUPS + " AS " + Tables.GROUPS);
5222f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                qb.setProjectionMap(returnGroupCountPerAccount ?
5223f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        sGroupsSummaryProjectionMapWithGroupCountPerAccount
5224f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        : sGroupsSummaryProjectionMap);
522543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                appendAccountFromParameter(qb, uri, true);
5226f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                groupBy = GroupsColumns.CONCRETE_ID;
5227ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5228ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5229ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5230b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
52310c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
5232b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
5233b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
5234b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
5235b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
523631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
5237d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
52382d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                String filter = null;
52392d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
52402d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                    filter = uri.getPathSegments().get(3);
52412d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                }
524231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
5243d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                if (limit != null) {
5244d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(limit);
524531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
524631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
524731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
524831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
52495b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                ArrayList<AggregationSuggestionParameter> parameters = null;
52505b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                List<String> query = uri.getQueryParameters("query");
52515b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                if (query != null && !query.isEmpty()) {
52525b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    parameters = new ArrayList<AggregationSuggestionParameter>(query.size());
52535b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    for (String parameter : query) {
52545b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        int offset = parameter.indexOf(':');
52555b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        parameters.add(offset == -1
52565b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                ? new AggregationSuggestionParameter(
525776dfa406e2cde19c824983c37fc92c1c5bf63eecDaniel Lehmann                                        AggregationSuggestions.PARAMETER_MATCH_NAME,
52585b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter)
52595b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                : new AggregationSuggestionParameter(
52605b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(0, offset),
52615b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(offset + 1)));
52625b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    }
52635b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                }
52645b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
5265763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
52667581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov
52677581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov                return mContactAggregator.queryAggregationSuggestions(qb, projection, contactId,
52685b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        maxSuggestions, filter, parameters);
526931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
527031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
5271eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
5272eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setTables(Tables.SETTINGS);
5273eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setProjectionMap(sSettingsProjectionMap);
527443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                appendAccountFromParameter(qb, uri, false);
5275e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
5276e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // When requesting specific columns, this query requires
5277e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // late-binding of the GroupMembership MIME-type.
5278b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                final String groupMembershipMimetypeId = Long.toString(mDbHelper
5279e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
528082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
5281b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_COUNT)) {
5282e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
5283e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
528482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
5285b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_WITH_PHONES)) {
5286e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
5287e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
5288e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
5289eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
5290eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
5291eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
529282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
52930a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
52945ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
52955ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
52965ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
529782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES_ID: {
52980a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
52994da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
53004da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(DataColumns.CONCRETE_ID + "=?");
53015ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
53025ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
53035ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
5304c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS: {
5305174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(
5306174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        db, uri, projection, limit);
5307c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5308c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
5309c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT: {
53102d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                String lookupKey = uri.getLastPathSegment();
5311174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                String filter = getQueryParameter(
5312174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        uri, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
5313174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchShortcutRefresh(
5314174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        db, projection, lookupKey, filter);
5315c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5316c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
53171b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS:
5318ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
53191b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
53201b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
53211b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
53221b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_WITH_PHONES:
5323ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
53241b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
53251b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.HAS_PHONE_NUMBER + "=1");
53261b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
53271b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
53281b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_FAVORITES:
5329ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
53301b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
53311b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.STARRED + "=1");
53321b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
53331b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
53341b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_GROUP_NAME:
5335ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
53361b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
533771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
53381b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
53391b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
53401b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
534146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITIES: {
5342a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
534346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
534446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
534546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
534646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITY_ID: {
534746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
5348a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
53494da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
53504da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
535146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
535246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
535346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
535409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            case PROVIDER_STATUS: {
535509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                return queryProviderStatus(uri, projection);
535609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
535709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5358d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES : {
5359d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
5360d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
5361d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
5362d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
5363d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
5364d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID : {
5365385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                long id = ContentUris.parseId(uri);
5366d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
5367d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
5368385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(id));
5369d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.appendWhere(Directory._ID + "=?");
5370d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
5371d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
5372d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
53737a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            case COMPLETE_NAME: {
53747a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                return completeName(uri, projection);
53757a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
53767a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
53774f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
5378f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
5379c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                        sortOrder, limit);
53804f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
53814f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
538209e69522745551522c55dff27424496f255def46Daniel Lehmann        qb.setStrict(true);
53837f786e5cbde9975b9632beb9b6d19eeef8a64cf1Dmitri Plotnikov
5384ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        Cursor cursor =
5385ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit);
5386ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (readBooleanQueryParameter(uri, ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, false)) {
5387ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            cursor = bundleLetterCountExtras(cursor, db, qb, selection, selectionArgs, sortOrder);
5388ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5389ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        return cursor;
53905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
53915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
53925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
53935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String selection, String[] selectionArgs, String sortOrder, String groupBy,
53945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String limit) {
5395038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        if (projection != null && projection.length == 1
5396038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                && BaseColumns._COUNT.equals(projection[0])) {
5397038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            qb.setProjectionMap(sCountProjectionMap);
5398038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
53995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
54005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sortOrder, limit);
54014f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
54024f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
54034f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
54044f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
54054f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
54064f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
540709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    /**
540809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     * Creates a single-row cursor containing the current status of the provider.
540909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     */
541009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private Cursor queryProviderStatus(Uri uri, String[] projection) {
541109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
541209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        RowBuilder row = cursor.newRow();
541309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
541409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            if (ProviderStatus.STATUS.equals(projection[i])) {
541509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mProviderStatus);
541609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            } else if (ProviderStatus.DATA1.equals(projection[i])) {
541709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mEstimatedStorageRequirement);
541809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
541909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        }
542009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        return cursor;
542109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    }
542209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5423a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /**
5424a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * Runs the query with the supplied contact ID and lookup ID.  If the query succeeds,
5425a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * it returns the resulting cursor, otherwise it returns null and the calling
5426a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * method needs to resolve the lookup key and rerun the query.
5427a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     */
5428a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private Cursor queryWithContactIdAndLookupKey(SQLiteQueryBuilder lookupQb,
5429a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            SQLiteDatabase db, Uri uri,
5430a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection, String selection, String[] selectionArgs,
5431a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String sortOrder, String groupBy, String limit,
5432a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn, long contactId, String lookupKeyColumn, String lookupKey) {
5433a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        String[] args;
5434a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (selectionArgs == null) {
5435a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[2];
5436a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        } else {
5437a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[selectionArgs.length + 2];
5438a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
5439a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5440a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[0] = String.valueOf(contactId);
5441a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[1] = Uri.encode(lookupKey);
5442a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        lookupQb.appendWhere(contactIdColumn + "=? AND " + lookupKeyColumn + "=?");
5443a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        Cursor c = query(db, lookupQb, projection, selection, args, sortOrder,
5444a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                groupBy, limit);
5445a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (c.getCount() != 0) {
5446a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return c;
5447a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5448a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5449a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        c.close();
5450a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return null;
5451a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
545209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5453bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov    private static final class AddressBookIndexQuery {
5454bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String LETTER = "letter";
5455bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String TITLE = "title";
5456bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String COUNT = "count";
5457ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5458bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
5459bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                LETTER, TITLE, COUNT
5460ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        };
5461ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5462bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_LETTER = 0;
5463bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_TITLE = 1;
5464bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_COUNT = 2;
5465bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
546624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // The first letter of the sort key column is what is used for the index headings, except
546724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // in the case of the user's profile, in which case it is empty.
546824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        public static final String SECTION_HEADING_TEMPLATE =
546924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                "(CASE WHEN %1$s=1 THEN '' ELSE SUBSTR(%2$s,1,1) END)";
547024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
5471de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa        public static final String ORDER_BY = LETTER + " COLLATE " + PHONEBOOK_COLLATOR_NAME;
5472ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
5473ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5474ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    /**
5475ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * Computes counts by the address book index titles and adds the resulting tally
5476ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * to the returned cursor as a bundle of extras.
5477ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     */
5478ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    private Cursor bundleLetterCountExtras(Cursor cursor, final SQLiteDatabase db,
5479ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            SQLiteQueryBuilder qb, String selection, String[] selectionArgs, String sortOrder) {
5480ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortKey;
5481ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5482ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // The sort order suffix could be something like "DESC".
5483ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // We want to preserve it in the query even though we will change
5484ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // the sort column itself.
5485ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortOrderSuffix = "";
5486ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (sortOrder != null) {
548724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
548824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // If the sort order contains one of the "is_profile" columns, we need to strip it out
548924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // first.
549024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            if (sortOrder.contains(Contacts.IS_USER_PROFILE)
549124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    || sortOrder.contains(RawContacts.RAW_CONTACT_IS_USER_PROFILE)) {
549224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                String[] splitOrderClauses = sortOrder.split(",");
549324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                StringBuilder rejoinedClause = new StringBuilder();
549424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                for (String orderClause : splitOrderClauses) {
549524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    if (!orderClause.contains(Contacts.IS_USER_PROFILE)
549624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                            && !orderClause.contains(RawContacts.RAW_CONTACT_IS_USER_PROFILE)) {
549724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        if (rejoinedClause.length() > 0) {
549824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                            rejoinedClause.append(", ");
549924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        }
550024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        rejoinedClause.append(orderClause.trim());
550124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    }
550224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                }
550324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                sortOrder = rejoinedClause.toString();
550424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
550524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
5506ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int spaceIndex = sortOrder.indexOf(' ');
5507ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            if (spaceIndex != -1) {
5508ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder.substring(0, spaceIndex);
5509ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortOrderSuffix = sortOrder.substring(spaceIndex);
5510ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            } else {
5511ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder;
5512ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
5513ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } else {
5514ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            sortKey = Contacts.SORT_KEY_PRIMARY;
5515ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5516ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5517bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        String locale = getLocale().toString();
5518ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        HashMap<String, String> projectionMap = Maps.newHashMap();
551924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
552024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // The user profile column varies depending on the view.
5521ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        String profileColumn = qb.getTables().contains(Views.CONTACTS)
552224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                ? Contacts.IS_USER_PROFILE
552324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                : RawContacts.RAW_CONTACT_IS_USER_PROFILE;
552424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        String sectionHeading = String.format(
552524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                AddressBookIndexQuery.SECTION_HEADING_TEMPLATE, profileColumn, sortKey);
5526bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.LETTER,
552724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                sectionHeading + " AS " + AddressBookIndexQuery.LETTER);
5528bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5529bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        /**
5530bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * Use the GET_PHONEBOOK_INDEX function, which is an android extension for SQLite3,
5531bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * to map the first letter of the sort key to a character that is traditionally
5532bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * used in phonebooks to represent that letter.  For example, in Korean it will
5533bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * be the first consonant in the letter; for Japanese it will be Hiragana rather
5534bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * than Katakana.
5535bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         */
5536ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.TITLE,
553724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                "GET_PHONEBOOK_INDEX(" + sectionHeading + ",'" + locale + "')"
5538bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                        + " AS " + AddressBookIndexQuery.TITLE);
5539ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.COUNT,
5540ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                "COUNT(" + Contacts._ID + ") AS " + AddressBookIndexQuery.COUNT);
5541ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        qb.setProjectionMap(projectionMap);
5542ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5543f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov        Cursor indexCursor = qb.query(db, AddressBookIndexQuery.COLUMNS, selection, selectionArgs,
5544ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY, null /* having */,
5545ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY + sortOrderSuffix);
5546ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5547ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        try {
5548f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            int groupCount = indexCursor.getCount();
5549ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            String titles[] = new String[groupCount];
5550ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int counts[] = new int[groupCount];
5551bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            int indexCount = 0;
5552bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            String currentTitle = null;
5553bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5554bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // Since GET_PHONEBOOK_INDEX is a many-to-1 function, we may end up
5555bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // with multiple entries for the same title.  The following code
5556bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // collapses those duplicates.
5557ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            for (int i = 0; i < groupCount; i++) {
5558f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov                indexCursor.moveToNext();
5559bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String title = indexCursor.getString(AddressBookIndexQuery.COLUMN_TITLE);
5560bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int count = indexCursor.getInt(AddressBookIndexQuery.COLUMN_COUNT);
5561bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                if (indexCount == 0 || !TextUtils.equals(title, currentTitle)) {
5562bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    titles[indexCount] = currentTitle = title;
5563bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount] = count;
5564bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    indexCount++;
5565bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                } else {
5566bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount - 1] += count;
5567bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                }
5568bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            }
5569bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5570bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            if (indexCount < groupCount) {
5571bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String[] newTitles = new String[indexCount];
5572bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(titles, 0, newTitles, 0, indexCount);
5573bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                titles = newTitles;
5574bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5575bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int[] newCounts = new int[indexCount];
5576bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(counts, 0, newCounts, 0, indexCount);
5577bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                counts = newCounts;
5578ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
5579ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5580e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return new AddressBookCursor((CrossProcessCursor) cursor, titles, counts);
5581ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } finally {
5582f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            indexCursor.close();
5583ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5584ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
5585ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
55862d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    /**
558792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Returns the contact Id for the contact identified by the lookupKey.
558892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Robust against changes in the lookup key: if the key has changed, will
558992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * look up the contact by the raw contact IDs or name encoded in the lookup
559092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * key.
55912d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     */
55922d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    public long lookupContactIdByLookupKey(SQLiteDatabase db, String lookupKey) {
55935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactLookupKey key = new ContactLookupKey();
55945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ArrayList<LookupKeySegment> segments = key.parse(lookupKey);
55955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
559692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        long contactId = -1;
559792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_SOURCE_ID)) {
559892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdBySourceIds(db, segments);
559992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
560092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
560192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
560292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
560392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
560492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        boolean hasRawContactIds =
560592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID);
560692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds) {
560792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdByRawContactIds(db, segments);
560892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
560992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
561092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
561192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
561292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
561392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds
561492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                || lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME)) {
56155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            contactId = lookupContactIdByDisplayNames(db, segments);
56165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
56175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
56185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return contactId;
56195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
56205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
56215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupBySourceIdQuery {
562243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String TABLE = Views.RAW_CONTACTS;
56235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
56245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
56255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
562643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
56275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
56285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.SOURCE_ID
56295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
56305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
56315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
563243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int ACCOUNT_TYPE_AND_DATA_SET = 1;
56335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
56345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int SOURCE_ID = 3;
56355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
56365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
56375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdBySourceIds(SQLiteDatabase db,
56385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
56395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
56405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(RawContacts.SOURCE_ID + " IN (");
56415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
56425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
564392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID) {
56445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
56455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
56465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
56475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
56485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
56495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
56505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
56515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupBySourceIdQuery.TABLE, LookupBySourceIdQuery.COLUMNS,
56525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
56535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
56545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
565543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                String accountTypeAndDataSet =
565643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE_AND_DATA_SET);
56575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME);
56585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
565943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        ContactLookupKey.getAccountHashCode(accountTypeAndDataSet, accountName);
56605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID);
56615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
56625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
566392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID
566492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
56655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(sourceId)) {
56665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupBySourceIdQuery.CONTACT_ID);
56675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
56685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
56695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
56705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
56715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
56725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
56735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
56745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
56755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
56765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
56775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
567892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByRawContactIdQuery {
567943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String TABLE = Views.RAW_CONTACTS;
56805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
56815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
56825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
568343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
56845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
568592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts._ID,
56865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
56875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
56885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
568943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int ACCOUNT_TYPE_AND_DATA_SET = 1;
56905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
569192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ID = 3;
56925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
56935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
569492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByRawContactIds(SQLiteDatabase db,
569592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            ArrayList<LookupKeySegment> segments) {
569692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
569792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(RawContacts._ID + " IN (");
56985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
56995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
570092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
570192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(segment.rawContactId);
570292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(",");
57035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
57045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
570592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
570692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
57075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
570892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        Cursor c = db.query(LookupByRawContactIdQuery.TABLE, LookupByRawContactIdQuery.COLUMNS,
570992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                 sb.toString(), null, null, null, null);
571092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        try {
571192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            while (c.moveToNext()) {
571243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                String accountTypeAndDataSet = c.getString(
571343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        LookupByRawContactIdQuery.ACCOUNT_TYPE_AND_DATA_SET);
571492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountName = c.getString(LookupByRawContactIdQuery.ACCOUNT_NAME);
571592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                int accountHashCode =
571643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        ContactLookupKey.getAccountHashCode(accountTypeAndDataSet, accountName);
571792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String rawContactId = c.getString(LookupByRawContactIdQuery.ID);
571892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
571992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
572092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID
572192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
572292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && segment.rawContactId.equals(rawContactId)) {
572392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        segment.contactId = c.getLong(LookupByRawContactIdQuery.CONTACT_ID);
572492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        break;
572592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    }
572692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                }
572792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
572892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        } finally {
572992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            c.close();
57305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
57315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
573292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return getMostReferencedContactId(segments);
573392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
573492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
573592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByDisplayNameQuery {
573692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
573792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
573892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String COLUMNS[] = {
573992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.CONTACT_ID,
574043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
574192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
574292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME
574392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        };
574492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
574592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int CONTACT_ID = 0;
574643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int ACCOUNT_TYPE_AND_DATA_SET = 1;
574792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_NAME = 2;
574892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int NORMALIZED_NAME = 3;
574992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
575092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
575192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByDisplayNames(SQLiteDatabase db,
575292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
57535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
57545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(NameLookupColumns.NORMALIZED_NAME + " IN (");
57555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
57565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
575792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
575892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
57595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
57605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
57615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
57625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
57635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
57645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
57655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                + " AND " + RawContacts.CONTACT_ID + " NOT NULL");
57665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
57675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupByDisplayNameQuery.TABLE, LookupByDisplayNameQuery.COLUMNS,
57685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
57695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
57705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
577143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                String accountTypeAndDataSet =
577243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE_AND_DATA_SET);
57735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME);
57745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
577543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        ContactLookupKey.getAccountHashCode(accountTypeAndDataSet, accountName);
57765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME);
57775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
57785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
577992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if ((segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
578092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID)
578192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
57825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(name)) {
57835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupByDisplayNameQuery.CONTACT_ID);
57845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
57855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
57865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
57875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
57885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
57895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
57905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
57915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
57925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
57935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
57945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
579592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private boolean lookupKeyContainsType(ArrayList<LookupKeySegment> segments, int lookupType) {
579692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
579792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            LookupKeySegment segment = segments.get(i);
579892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == lookupType) {
579992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return true;
580092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
580192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
580292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
580392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return false;
580492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
580592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
5806ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateLookupKeyForRawContact(SQLiteDatabase db, long rawContactId) {
5807ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov        mContactAggregator.updateLookupKeyForRawContact(db, rawContactId);
5808ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    }
5809ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov
58105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /**
58115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     * Returns the contact ID that is mentioned the highest number of times.
58125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     */
58135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long getMostReferencedContactId(ArrayList<LookupKeySegment> segments) {
58145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Collections.sort(segments);
58155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
58165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long bestContactId = -1;
58175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int bestRefCount = 0;
58185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
58195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = -1;
58205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int count = 0;
58215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
58225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int segmentCount = segments.size();
58235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segmentCount; i++) {
58245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
58255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.contactId != -1) {
58265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segment.contactId == contactId) {
58275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count++;
58285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                } else {
58295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (count > bestRefCount) {
58305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestContactId = contactId;
58315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestRefCount = count;
58325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
58335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    contactId = segment.contactId;
58345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count = 1;
58355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
58365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
58375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
58385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count > bestRefCount) {
58395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return contactId;
58405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
58415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return bestContactId;
58425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
58435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
58445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
5845763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
5846763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            String[] projection) {
58474928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa        setTablesAndProjectionMapForContacts(qb, uri, projection, false);
58482f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    }
58492f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
58502f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    /**
58514928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * @param includeDataUsageStat true when the table should include DataUsageStat table.
58524928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * Note that this uses INNER JOIN instead of LEFT OUTER JOIN, so some of data in Contacts
58534928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * may be dropped.
58542f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     */
58552f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
58564928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            String[] projection, boolean includeDataUsageStat) {
585782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5858ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.CONTACTS);
58592f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
58602f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa        // Just for frequently contacted contacts in Strequent Uri handling.
58614928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa        if (includeDataUsageStat) {
58622f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            sb.append(" INNER JOIN " +
5863ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                    Views.DATA_USAGE_STAT + " AS " + Tables.DATA_USAGE_STAT +
58642f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    " ON (" +
58652f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    DbQueryUtils.concatenateClauses(
58662f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                            DataUsageStatColumns.CONCRETE_TIMES_USED + " > 0",
58674928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            RawContacts.CONTACT_ID + "=" + Views.CONTACTS + "." + Contacts._ID) +
58682f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    ")");
58692f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa        }
58702f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
58717ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
58727ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
5873916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setTables(sb.toString());
5874916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionMap);
5875916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    }
5876916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5877916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /**
5878916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * Finds name lookup records matching the supplied filter, picks one arbitrary match per
5879916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * contact and joins that with other contacts tables.
5880916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     */
5881916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private void setTablesAndProjectionMapForContactsWithSnippet(SQLiteQueryBuilder qb, Uri uri,
58827ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov            String[] projection, String filter, long directoryId) {
58837ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov
58847ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
5885ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.CONTACTS);
5886916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
588703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        if (filter != null) {
588803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            filter = filter.trim();
588903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        }
589003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
589130cc766756461da8d53933f88ea01dd2272a90ebDmitri Plotnikov        if (TextUtils.isEmpty(filter) || (directoryId != -1 && directoryId != Directory.DEFAULT)) {
589230cc766756461da8d53933f88ea01dd2272a90ebDmitri Plotnikov            sb.append(" JOIN (SELECT NULL AS " + SearchSnippetColumns.SNIPPET + " WHERE 0)");
58935e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        } else {
58945e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            appendSearchIndexJoin(sb, uri, projection, filter);
58955e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        }
58967ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
58977ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
589803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        qb.setTables(sb.toString());
589903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionWithSnippetMap);
590003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    }
5901916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
590203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private void appendSearchIndexJoin(
590303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            StringBuilder sb, Uri uri, String[] projection, String filter) {
5904916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5905174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET)) {
590603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String[] args = null;
590703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String snippetArgs =
590803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                    getQueryParameter(uri, SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY);
590903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            if (snippetArgs != null) {
591003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                args = snippetArgs.split(",");
591103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
591203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
59135e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String startMatch = args != null && args.length > 0 ? args[0]
59145e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_START_MATCH;
59155e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String endMatch = args != null && args.length > 1 ? args[1]
59165e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_END_MATCH;
59175e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String ellipsis = args != null && args.length > 2 ? args[2]
59185e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_ELLIPSIS;
59195e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            int maxTokens = args != null && args.length > 3 ? Integer.parseInt(args[3])
59205e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_MAX_TOKENS;
59215e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
5922174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            appendSearchIndexJoin(
5923174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                    sb, filter, true, startMatch, endMatch, ellipsis, maxTokens);
5924174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        } else {
5925174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            appendSearchIndexJoin(sb, filter, false, null, null, null, 0);
5926174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
5927174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    }
5928174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
5929174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    public void appendSearchIndexJoin(StringBuilder sb, String filter,
5930174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            boolean snippetNeeded, String startMatch, String endMatch, String ellipsis,
5931174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            int maxTokens) {
5932174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        boolean isEmailAddress = false;
5933174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String emailAddress = null;
5934174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        boolean isPhoneNumber = false;
5935174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String phoneNumber = null;
5936174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String numberE164 = null;
5937174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
59383716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // If the query consists of a single word, we can do snippetizing after-the-fact for a
59393716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // performance boost.
59403716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        boolean singleTokenSearch = filter.split(QUERY_TOKENIZER_REGEX).length == 1;
59413716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
5942174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (filter.indexOf('@') != -1) {
5943174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            emailAddress = mDbHelper.extractAddressFromEmailAddress(filter);
5944174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            isEmailAddress = !TextUtils.isEmpty(emailAddress);
5945174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        } else {
5946174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            isPhoneNumber = isPhoneNumber(filter);
594704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            if (isPhoneNumber) {
594804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                phoneNumber = PhoneNumberUtils.normalizeNumber(filter);
594904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                numberE164 = PhoneNumberUtils.formatNumberToE164(phoneNumber,
595004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                        mDbHelper.getCountryIso());
595104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            }
5952174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
5953174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
5954174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        sb.append(" JOIN (SELECT " + SearchIndexColumns.CONTACT_ID + " AS snippet_contact_id");
5955174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (snippetNeeded) {
59565e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            sb.append(", ");
59575e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            if (isEmailAddress) {
59583d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append("ifnull(");
59595e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, startMatch);
596004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("||(SELECT MIN(" + Email.ADDRESS + ")");
596104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" FROM " + Tables.DATA_JOIN_RAW_CONTACTS);
596204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" WHERE  " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
596304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + RawContacts.CONTACT_ID + " AND " + Email.ADDRESS + " LIKE ");
596404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                DatabaseUtils.appendEscapedSQLString(sb, filter + "%");
596504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(")||");
59663d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, endMatch);
59673d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append(",");
59683716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
59693716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                // Optimization for single-token search.
59703716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                if (singleTokenSearch) {
59713716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    sb.append(SearchIndexColumns.CONTENT);
59723716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                } else {
59733716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
59743716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                }
59753d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append(")");
59763d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov            } else if (isPhoneNumber) {
59773d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append("ifnull(");
59783d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, startMatch);
597904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("||(SELECT MIN(" + Phone.NUMBER + ")");
598004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" FROM " +
598104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                        Tables.DATA_JOIN_RAW_CONTACTS + " JOIN " + Tables.PHONE_LOOKUP);
598204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" ON " + DataColumns.CONCRETE_ID);
598304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + Tables.PHONE_LOOKUP + "." + PhoneLookupColumns.DATA_ID);
598404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" WHERE  " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
598504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + RawContacts.CONTACT_ID);
598604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" AND " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
598704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(phoneNumber);
598804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("%'");
598904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                if (!TextUtils.isEmpty(numberE164)) {
599004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append(" OR " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
599104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append(numberE164);
599204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append("%'");
599304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                }
599404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(")||");
59955e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, endMatch);
59965e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                sb.append(",");
59973716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
59983716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                // Optimization for single-token search.
59993716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                if (singleTokenSearch) {
60003716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    sb.append(SearchIndexColumns.CONTENT);
60013716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                } else {
60023716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
60033716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                }
60045e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                sb.append(")");
600503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            } else {
600604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                final String normalizedFilter = NameNormalizer.normalize(filter);
600704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                if (!TextUtils.isEmpty(normalizedFilter)) {
60083716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    // Optimization for single-token search.
60093716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    if (singleTokenSearch) {
60103716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(SearchIndexColumns.CONTENT);
60113716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    } else {
60123716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("(CASE WHEN EXISTS (SELECT 1 FROM ");
60133716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.RAW_CONTACTS + " AS rc INNER JOIN ");
60143716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.NAME_LOOKUP + " AS nl ON (rc." + RawContacts._ID);
60153716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("=nl." + NameLookupColumns.RAW_CONTACT_ID);
60163716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(") WHERE nl." + NameLookupColumns.NORMALIZED_NAME);
60173716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(" GLOB '" + normalizedFilter + "*' AND ");
60183716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("nl." + NameLookupColumns.NAME_TYPE + "=");
60193716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(NameLookupType.NAME_COLLATION_KEY + " AND ");
60203716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
60213716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("=rc." + RawContacts.CONTACT_ID);
60223716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(") THEN NULL ELSE ");
60233716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
60243716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(" END)");
60253716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    }
602604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                } else {
602704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append("NULL");
602804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                }
602903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
60305e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            sb.append(" AS " + SearchSnippetColumns.SNIPPET);
60315e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        }
603203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
60335e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(" FROM " + Tables.SEARCH_INDEX);
60345e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(" WHERE ");
60355e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(Tables.SEARCH_INDEX + " MATCH ");
60365e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        if (isEmailAddress) {
60372352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb, "\"" + sanitizeMatch(filter) + "*\"");
60383d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov        } else if (isPhoneNumber) {
60392352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb,
604004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    "\"" + sanitizeMatch(filter) + "*\" OR \"" + phoneNumber + "*\""
60412352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                            + (numberE164 != null ? " OR \"" + numberE164 + "\"" : ""));
604203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        } else {
60432352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filter) + "*");
60449c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        }
604503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        sb.append(") ON (" + Contacts._ID + "=snippet_contact_id)");
6046a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov    }
6047a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov
60482352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov    private String sanitizeMatch(String filter) {
60492352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov        // TODO more robust preprocessing of match expressions
60502352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov        return filter.replace('-', ' ').replace('\"', ' ');
60512352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov    }
60522352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov
60535e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov    private void appendSnippetFunction(
60545e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            StringBuilder sb, String startMatch, String endMatch, String ellipsis, int maxTokens) {
60555e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append("snippet(" + Tables.SEARCH_INDEX + ",");
60565e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, startMatch);
60575e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",");
60585e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, endMatch);
60595e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",");
60605e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, ellipsis);
60615e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
60625e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        // The index of the column used for the snippet, "content"
60635e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",1,");
60645e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(maxTokens);
60655e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(")");
60665e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov    }
60675e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
6068763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForRawContacts(SQLiteQueryBuilder qb, Uri uri) {
6069763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        StringBuilder sb = new StringBuilder();
6070ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.RAW_CONTACTS);
6071763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setTables(sb.toString());
6072763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setProjectionMap(sRawContactsProjectionMap);
607343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        appendAccountFromParameter(qb, uri, true);
6074763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    }
6075763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
6076a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForRawEntities(SQLiteQueryBuilder qb, Uri uri) {
6077ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        qb.setTables(Views.RAW_ENTITIES);
6078a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sRawEntityProjectionMap);
607943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        appendAccountFromParameter(qb, uri, true);
608046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    }
608146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
608282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
608382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String[] projection, boolean distinct) {
608446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        setTablesAndProjectionMapForData(qb, uri, projection, distinct, null);
608546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
608646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
608746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
608846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * @param usageType when non-null {@link Tables#DATA_USAGE_STAT} is joined with the specified
608946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * type.
609046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
609146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
609246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            String[] projection, boolean distinct, Integer usageType) {
609382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
6094ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.DATA);
609582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sb.append(" data");
609682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
6097a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, RawContacts.CONTACT_ID);
6098a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
6099a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
6100a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
61013296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
610246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (usageType != null) {
610346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            appendDataUsageStatJoin(sb, usageType, DataColumns.CONCRETE_ID);
610446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
610546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
610682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
6107f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov
6108f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        boolean useDistinct = distinct
6109f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov                || !mDbHelper.isInProjection(projection, DISTINCT_DATA_PROHIBITING_COLUMNS);
6110f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setDistinct(useDistinct);
6111f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setProjectionMap(useDistinct ? sDistinctDataProjectionMap : sDataProjectionMap);
611243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        appendAccountFromParameter(qb, uri, true);
6113ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    }
6114ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
61150a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb,
61160a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String[] projection) {
61170a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        StringBuilder sb = new StringBuilder();
6118ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.DATA);
61190a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        sb.append(" data");
6120a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
6121a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
61220a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
6123a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
6124a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sStatusUpdatesProjectionMap);
6125a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6126a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
61273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void setTablesAndProjectionMapForStreamItems(SQLiteQueryBuilder qb) {
61281dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro        qb.setTables(Tables.STREAM_ITEMS
61291dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + " JOIN " + Tables.RAW_CONTACTS + " ON ("
61301dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + StreamItemsColumns.CONCRETE_RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID
61311dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + ") JOIN " + Tables.CONTACTS + " ON ("
61321dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + RawContactsColumns.CONCRETE_CONTACT_ID + "=" + ContactsColumns.CONCRETE_ID + ")");
61333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        qb.setProjectionMap(sStreamItemsProjectionMap);
61343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
61353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
61363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void setTablesAndProjectionMapForStreamItemPhotos(SQLiteQueryBuilder qb) {
61371dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro        qb.setTables(Tables.PHOTO_FILES
61381dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + " JOIN " + Tables.STREAM_ITEM_PHOTOS + " ON ("
61391dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + StreamItemPhotosColumns.CONCRETE_PHOTO_FILE_ID + "="
61401dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + PhotoFilesColumns.CONCRETE_ID
61411dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + ") JOIN " + Tables.STREAM_ITEMS + " ON ("
61421dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "="
61431dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + StreamItemsColumns.CONCRETE_ID + ")");
61443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        qb.setProjectionMap(sStreamItemPhotosProjectionMap);
61453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
61463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
6147a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForEntities(SQLiteQueryBuilder qb, Uri uri,
6148a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection) {
6149a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
6150ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.ENTITIES);
6151a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(" data");
6152a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6153a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts.Entity.CONTACT_ID);
6154a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
6155a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, Contacts.Entity.DATA_ID);
6156a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, Contacts.Entity.DATA_ID);
6157a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6158a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
6159a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sEntityProjectionMap);
616043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        appendAccountFromParameter(qb, uri, true);
6161a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6162a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6163a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactStatusUpdateJoin(StringBuilder sb, String[] projection,
6164a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String lastStatusUpdateIdColumn) {
6165a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection,
6166a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS,
6167a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
6168a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
6169a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
6170a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP)) {
6171a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
6172a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    + ContactsStatusUpdatesColumns.ALIAS +
6173a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + lastStatusUpdateIdColumn + "="
6174a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
61750a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
6176a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
61770a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
6178a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataStatusUpdateJoin(StringBuilder sb, String[] projection,
6179a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
6180b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
61810a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS,
61820a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_RES_PACKAGE,
61830a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_ICON,
61840a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_LABEL,
61850a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_TIMESTAMP)) {
61860a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
6187a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdatesColumns.CONCRETE_DATA_ID + "="
6188a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + dataIdColumn + ")");
61890a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
6190a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6191a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
619246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private void appendDataUsageStatJoin(StringBuilder sb, int usageType, String dataIdColumn) {
619346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        sb.append(" LEFT OUTER JOIN " + Tables.DATA_USAGE_STAT +
619446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "=" + dataIdColumn +
619546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                " AND " + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "=" + usageType + ")");
619646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
619746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
6198a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactPresenceJoin(StringBuilder sb, String[] projection,
6199a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn) {
6200a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection,
6201a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_PRESENCE, Contacts.CONTACT_CHAT_CAPABILITY)) {
6202a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
6203a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + contactIdColumn + " = "
6204a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + AggregatedPresenceColumns.CONCRETE_CONTACT_ID + ")");
6205a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
6206a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6207a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6208a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataPresenceJoin(StringBuilder sb, String[] projection,
6209a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
6210a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, Data.PRESENCE, Data.CHAT_CAPABILITY)) {
6211a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
6212a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdates.DATA_ID + "=" + dataIdColumn + ")");
6213a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
6214a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6215a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
621624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private boolean appendLocalDirectorySelectionIfNeeded(SQLiteQueryBuilder qb, long directoryId) {
6217385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directoryId == Directory.DEFAULT) {
6218385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " IN " + Tables.DEFAULT_DIRECTORY);
621924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            return true;
6220385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directoryId == Directory.LOCAL_INVISIBLE){
6221385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " NOT IN " + Tables.DEFAULT_DIRECTORY);
622224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            return true;
622324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
622424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        return false;
622524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
622624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
622724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private void appendProfileRestriction(SQLiteQueryBuilder qb, Uri uri, String profileColumn,
62286ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro            boolean andRequired, boolean suppressProfileCheck) {
62296ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro        if (!shouldIncludeProfile(uri, suppressProfileCheck)) {
623024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            qb.appendWhere((andRequired ? " AND (" : "")
623124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    + profileColumn + " IS NULL OR "
623224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    + profileColumn + "=0"
623324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    + (andRequired ? ")" : ""));
6234385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        }
6235385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov    }
6236385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov
62376ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro    private String prependProfileSortIfNeeded(Uri uri, String sortOrder,
62386ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro            boolean suppressProfileCheck) {
62396ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro        if (shouldIncludeProfile(uri, suppressProfileCheck)) {
624024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            if (TextUtils.isEmpty(sortOrder)) {
624124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                return Contacts.IS_USER_PROFILE + " DESC";
624224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            } else {
624324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                return Contacts.IS_USER_PROFILE + " DESC, " + sortOrder;
624424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
624524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
624624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        return sortOrder;
624724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
624824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
62496ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro    private boolean shouldIncludeProfile(Uri uri, boolean suppressProfileCheck) {
625024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // The user's profile may be returned alongside other contacts if it was requested and
625124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // the calling application has permission to read profile data.
6252377850d2dfd28eaf1b22273a50cfe066f6667ab9Dave Santoro        boolean profileRequested = readBooleanQueryParameter(uri, ContactsContract.ALLOW_PROFILE,
625324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                false);
62546ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro        if (profileRequested && !suppressProfileCheck) {
625524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermission(false);
625624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
625724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        return profileRequested;
625824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
625924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
626043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri,
626143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            boolean includeDataSet) {
6262f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
6263f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
626443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        final String dataSet = getQueryParameter(uri, RawContacts.DATA_SET);
6265e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6266e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
6267e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
6268e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
6269fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
6270fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
6271e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
6272e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6273e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
6274e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
6275e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
6276e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
627743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            String toAppend = RawContacts.ACCOUNT_NAME + "="
62784a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
62794a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
628043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + DatabaseUtils.sqlEscapeString(accountType);
628143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            if (includeDataSet) {
628243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                if (dataSet == null) {
628343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    toAppend += " AND " + RawContacts.DATA_SET + " IS NULL";
628443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                } else {
628543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    toAppend += " AND " + RawContacts.DATA_SET + "=" +
628643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            DatabaseUtils.sqlEscapeString(dataSet);
628743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                }
628843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
628943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            qb.appendWhere(toAppend);
62904a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        } else {
62914a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere("1");
62924a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
62934a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
62944a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
6295e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    private String appendAccountToSelection(Uri uri, String selection) {
6296f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
6297f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
629843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        final String dataSet = getQueryParameter(uri, RawContacts.DATA_SET);
6299e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6300e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
6301e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
6302e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
6303fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
6304fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
6305e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
6306e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6307e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
6308e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
6309e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
6310e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
6311e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "="
6312e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
6313e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + RawContacts.ACCOUNT_TYPE + "="
6314e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountType));
631543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            if (!TextUtils.isEmpty(dataSet)) {
631643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                selectionSb.append(" AND " + RawContacts.DATA_SET + "=")
631743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        .append(DatabaseUtils.sqlEscapeString(dataSet));
631843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
6319e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (!TextUtils.isEmpty(selection)) {
6320e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(" AND (");
6321e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(selection);
6322e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(')');
6323e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
6324e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selectionSb.toString();
6325e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } else {
6326e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selection;
6327e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
6328e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    }
6329e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
63307e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
6331c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
6332c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *
6333c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
6334c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
6335c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     */
6336f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private String getLimit(Uri uri) {
63372e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limitParam = getQueryParameter(uri, ContactsContract.LIMIT_PARAM_KEY);
6338c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limitParam == null) {
6339c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
6340c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
6341c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // make sure that the limit is a non-negative integer
6342c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        try {
6343c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int l = Integer.parseInt(limitParam);
6344c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (l < 0) {
6345c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
6346c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return null;
6347c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
6348c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return String.valueOf(l);
6349c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        } catch (NumberFormatException ex) {
6350c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
6351c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
6352c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
6353c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
6354c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
6355b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    @Override
6356f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
6357415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
6358f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (mode.equals("r")) {
6359f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            waitForAccess(mReadAccessLatch);
6360f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } else {
6361f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            waitForAccess(mWriteAccessLatch);
6362f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
6363415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
6364b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        int match = sUriMatcher.match(uri);
6365b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        switch (match) {
6366a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
6367afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                SQLiteDatabase db = mDbHelper.getReadableDatabase();
636824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
6369afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForRawContact(db, rawContactId, false);
6370afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                return openPhotoAssetFile(db, uri, mode,
637124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        Data._ID + "=" + Contacts.PHOTO_ID + " AND " +
637224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                                RawContacts.CONTACT_ID + "=?",
637324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        new String[]{String.valueOf(rawContactId)});
6374e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            }
6375b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
6376f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_ID_DISPLAY_PHOTO: {
6377f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
6378f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
6379f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Display photos retrieved by contact ID can only be read.");
6380f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6381afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                SQLiteDatabase db = mDbHelper.getReadableDatabase();
6382f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long contactId = Long.parseLong(uri.getPathSegments().get(1));
6383afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
6384afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                Cursor c = db.query(Tables.CONTACTS,
6385f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{Contacts.PHOTO_FILE_ID},
6386f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        Contacts._ID + "=?", new String[]{String.valueOf(contactId)},
6387f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        null, null, null);
6388f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
6389f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.moveToFirst();
6390f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    long photoFileId = c.getLong(0);
6391f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForRead(photoFileId);
6392f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
6393f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
6394f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6395f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6396f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6397f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_DISPLAY_PHOTO:
6398f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_ID_DISPLAY_PHOTO: {
6399f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
6400f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
6401f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Display photos retrieved by contact lookup key can only be read.");
6402f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6403f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                List<String> pathSegments = uri.getPathSegments();
6404f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                int segmentCount = pathSegments.size();
6405f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (segmentCount < 4) {
6406f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
6407f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Missing a lookup key", uri));
6408f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6409afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                SQLiteDatabase db = mDbHelper.getReadableDatabase();
6410f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                String lookupKey = pathSegments.get(2);
6411f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                String[] projection = new String[]{Contacts.PHOTO_FILE_ID};
6412f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (segmentCount == 5) {
6413f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    long contactId = Long.parseLong(pathSegments.get(3));
6414afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                    enforceProfilePermissionForContact(db, contactId, false);
6415f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
6416f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
6417afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
6418f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            projection, null, null, null, null, null,
6419f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
6420f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (c != null) {
6421f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        try {
6422f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            c.moveToFirst();
6423f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            long photoFileId = c.getLong(c.getColumnIndex(Contacts.PHOTO_FILE_ID));
6424f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            return openDisplayPhotoForRead(photoFileId);
6425f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        } finally {
6426f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            c.close();
6427f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
6428f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
6429f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6430f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6431f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
6432f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                setTablesAndProjectionMapForContacts(qb, uri, projection);
6433afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                long contactId = lookupContactIdByLookupKey(db, lookupKey);
6434afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
6435afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                Cursor c = qb.query(db, projection, Contacts._ID + "=?",
6436f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{String.valueOf(contactId)}, null, null, null);
6437f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
6438f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.moveToFirst();
6439f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    long photoFileId = c.getLong(c.getColumnIndex(Contacts.PHOTO_FILE_ID));
6440f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForRead(photoFileId);
6441f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
6442f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
6443f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6444f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6445f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6446f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case RAW_CONTACTS_ID_DISPLAY_PHOTO: {
6447f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
6448f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                boolean writeable = !mode.equals("r");
6449afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                SQLiteDatabase db = mDbHelper.getReadableDatabase();
6450afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForRawContact(db, rawContactId, writeable);
6451f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6452f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Find the primary photo data record for this raw contact.
6453f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
6454f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                String[] projection = new String[]{Data._ID, Photo.PHOTO_FILE_ID};
6455f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
6456afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                Cursor c = qb.query(db, projection,
6457f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        Data.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?",
6458f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{String.valueOf(rawContactId), Photo.CONTENT_ITEM_TYPE},
6459f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        null, null, Data.IS_PRIMARY + " DESC");
6460f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long dataId = 0;
6461f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long photoFileId = 0;
6462f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
6463f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (c.getCount() >= 1) {
6464f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        c.moveToFirst();
6465f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        dataId = c.getLong(0);
6466f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        photoFileId = c.getLong(1);
6467f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
6468f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
6469f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
6470f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6471f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6472f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // If writeable, open a writeable file descriptor that we can monitor.
6473f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // When the caller finishes writing content, we'll process the photo and
6474f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // update the data record.
6475f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (writeable) {
6476f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForWrite(rawContactId, dataId, uri, mode);
6477f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } else {
6478f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForRead(photoFileId);
6479f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6480f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6481f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6482f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case DISPLAY_PHOTO: {
6483f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long photoFileId = ContentUris.parseId(uri);
6484f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
6485f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
6486f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Display photos retrieved by key can only be read.");
6487f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6488f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                return openDisplayPhotoForRead(photoFileId);
6489f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6490f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6491e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            case DATA_ID: {
6492afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                SQLiteDatabase db = mDbHelper.getReadableDatabase();
649324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long dataId = Long.parseLong(uri.getPathSegments().get(1));
6494afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForData(db, dataId, false);
6495afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                return openPhotoAssetFile(db, uri, mode,
6496e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        Data._ID + "=? AND " + Data.MIMETYPE + "='" + Photo.CONTENT_ITEM_TYPE + "'",
649724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        new String[]{String.valueOf(dataId)});
6498d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
6499d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6500fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            case PROFILE_AS_VCARD: {
6501fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // When opening a contact as file, we pass back contents as a
6502fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // vCard-encoded stream. We build into a local buffer first,
6503fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // then pipe into MemoryFile once the exact size is known.
6504fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
6505fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(uri, localStream, null, null);
6506fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                return buildAssetFileDescriptor(localStream);
6507fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            }
650842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
6509fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            case CONTACTS_AS_VCARD: {
651042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // When opening a contact as file, we pass back contents as a
651142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // vCard-encoded stream. We build into a local buffer first,
651242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // then pipe into MemoryFile once the exact size is known.
651342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
6514fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(uri, localStream, null, null);
6515f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
651642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
651742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
651842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
651949d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                SQLiteDatabase db = mDbHelper.getReadableDatabase();
652042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKeys = uri.getPathSegments().get(2);
652142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String[] loopupKeyList = lookupKeys.split(":");
652242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final StringBuilder inBuilder = new StringBuilder();
6523fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                Uri queryUri = Contacts.CONTENT_URI;
652442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                int index = 0;
6525fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen
6526d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // SQLite has limits on how many parameters can be used
6527d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // so the IDs are concatenated to a query string here instead
652842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                for (String lookupKey : loopupKeyList) {
652942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    if (index == 0) {
6530d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append("(");
653142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    } else {
6532d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append(",");
653342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    }
653424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    long contactId = lookupContactIdByLookupKey(db, lookupKey);
6535afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                    enforceProfilePermissionForContact(db, contactId, false);
653624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    inBuilder.append(contactId);
6537fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                    if (mProfileIdCache.profileContactId == contactId) {
6538fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                        queryUri = queryUri.buildUpon().appendQueryParameter(
6539377850d2dfd28eaf1b22273a50cfe066f6667ab9Dave Santoro                                ContactsContract.ALLOW_PROFILE, "true").build();
6540fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                    }
654142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    index++;
654242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                }
654342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                inBuilder.append(')');
654442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + " IN " + inBuilder.toString();
6545d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6546d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // When opening a contact as file, we pass back contents as a
6547d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // vCard-encoded stream. We build into a local buffer first,
6548d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // then pipe into MemoryFile once the exact size is known.
6549d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
6550fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(queryUri, localStream, selection, null);
6551f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
6552d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
6553b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
6554b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            default:
6555fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new FileNotFoundException(mDbHelper.exceptionMessage("File does not exist",
6556fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        uri));
6557b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        }
6558b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
6559b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
6560afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro    private AssetFileDescriptor openPhotoAssetFile(SQLiteDatabase db, Uri uri, String mode,
6561afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro            String selection, String[] selectionArgs)
6562e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throws FileNotFoundException {
6563e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        if (!"r".equals(mode)) {
6564e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throw new FileNotFoundException(mDbHelper.exceptionMessage("Mode " + mode
6565e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                    + " not supported.", uri));
6566e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        }
6567e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
6568e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        String sql =
6569ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                "SELECT " + Photo.PHOTO + " FROM " + Views.DATA +
6570e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                " WHERE " + selection;
657108ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        try {
6572f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
6573f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    DatabaseUtils.blobFileDescriptorForQuery(db, sql, selectionArgs));
657408ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        } catch (SQLiteDoneException e) {
657508ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            // this will happen if the DB query returns no rows (i.e. contact does not exist)
657608ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            throw new FileNotFoundException(uri.toString());
657708ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        }
6578e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov    }
6579e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
6580f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
6581f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Opens a display photo from the photo store for reading.
6582f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param photoFileId The display photo file ID
6583f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @return An asset file descriptor that allows the file to be read.
6584f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @throws FileNotFoundException If no photo file for the given ID exists.
6585f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
6586f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private AssetFileDescriptor openDisplayPhotoForRead(long photoFileId)
6587f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            throws FileNotFoundException {
6588f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        PhotoStore.Entry entry = mPhotoStore.get(photoFileId);
6589f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (entry != null) {
6590f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            return makeAssetFileDescriptor(
6591f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    ParcelFileDescriptor.open(new File(entry.path),
6592f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            ParcelFileDescriptor.MODE_READ_ONLY),
6593f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    entry.size);
6594f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } else {
6595f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
6596f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            throw new FileNotFoundException("No photo file found for ID " + photoFileId);
6597f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
6598f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
6599f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6600f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
6601f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Opens a file descriptor for a photo to be written.  When the caller completes writing
6602f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * to the file (closing the output stream), the image will be parsed out and processed.
6603f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * If processing succeeds, the given raw contact ID's primary photo record will be
6604f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * populated with the inserted image (if no primary photo record exists, the data ID can
6605f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * be left as 0, and a new data record will be inserted).
6606f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param rawContactId Raw contact ID this photo entry should be associated with.
6607f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param dataId Data ID for a photo mimetype that will be updated with the inserted
6608f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     image.  May be set to 0, in which case the inserted image will trigger creation
6609f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     of a new primary photo image data row for the raw contact.
6610f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param uri The URI being used to access this file.
6611f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param mode Read/write mode string.
6612f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @return An asset file descriptor the caller can use to write an image file for the
6613f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     raw contact.
6614f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
6615f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private AssetFileDescriptor openDisplayPhotoForWrite(long rawContactId, long dataId, Uri uri,
6616f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            String mode) {
6617f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        try {
6618f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            return new AssetFileDescriptor(new MonitoredParcelFileDescriptor(rawContactId, dataId,
6619f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    ParcelFileDescriptor.open(File.createTempFile("img", null),
6620f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            ContentResolver.modeToMode(uri, mode))),
6621f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    0, AssetFileDescriptor.UNKNOWN_LENGTH);
6622f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } catch (IOException ioe) {
6623f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            Log.e(TAG, "Could not create temp image file in mode " + mode);
6624f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            return null;
6625f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
6626f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
6627f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6628f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
6629f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Parcel file descriptor wrapper that monitors when the file is closed.
6630f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * If the file contains a valid image, the image is either inserted into the given
6631f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * raw contact or updated in the given data row.
6632f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
6633f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private class MonitoredParcelFileDescriptor extends ParcelFileDescriptor {
6634f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        private final long mRawContactId;
6635f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        private final long mDataId;
6636f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        private MonitoredParcelFileDescriptor(long rawContactId, long dataId,
6637f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                ParcelFileDescriptor descriptor) {
6638f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            super(descriptor);
6639f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            mRawContactId = rawContactId;
6640f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            mDataId = dataId;
6641f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
6642f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6643f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        @Override
6644f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        public void close() throws IOException {
6645f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            try {
6646f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Check to see whether a valid image was written out.
6647f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                Bitmap b = BitmapFactory.decodeFileDescriptor(getFileDescriptor());
6648f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (b != null) {
6649f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    PhotoProcessor processor = new PhotoProcessor(b, mMaxDisplayPhotoDim,
6650f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            mMaxThumbnailPhotoDim);
6651f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6652f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    // Store the compressed photo in the photo store.
6653f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    long photoFileId = mPhotoStore.insert(processor);
6654f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6655f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    // Depending on whether we already had a data row to attach the photo to,
6656f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    // do an update or insert.
6657f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (mDataId != 0) {
6658f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Update the data record with the new photo.
6659f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        ContentValues updateValues = new ContentValues();
6660f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6661f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Signal that photo processing has already been handled.
6662f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        updateValues.put(DataRowHandlerForPhoto.SKIP_PROCESSING_KEY, true);
6663f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6664f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        if (photoFileId != 0) {
6665f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            updateValues.put(Photo.PHOTO_FILE_ID, photoFileId);
6666f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
6667f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        updateValues.put(Photo.PHOTO, processor.getThumbnailPhotoBytes());
6668f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        update(ContentUris.withAppendedId(Data.CONTENT_URI, mDataId), updateValues,
6669f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                null, null);
6670f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    } else {
6671f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Insert a new primary data record with the photo.
6672f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        ContentValues insertValues = new ContentValues();
6673f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6674f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Signal that photo processing has already been handled.
6675f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(DataRowHandlerForPhoto.SKIP_PROCESSING_KEY, true);
6676f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6677f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
6678f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Data.IS_PRIMARY, 1);
6679f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        if (photoFileId != 0) {
6680f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            insertValues.put(Photo.PHOTO_FILE_ID, photoFileId);
6681f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
6682f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Photo.PHOTO, processor.getThumbnailPhotoBytes());
6683f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insert(RawContacts.CONTENT_URI.buildUpon()
6684f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                .appendPath(String.valueOf(mRawContactId))
6685f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                .appendPath(RawContacts.Data.CONTENT_DIRECTORY).build(),
6686f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                insertValues);
6687f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
6688f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6689f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            } finally {
6690f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                super.close();
6691f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6692f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
6693f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
6694f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6695d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String CONTACT_MEMORY_FILE_NAME = "contactAssetFile";
6696d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6697d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
6698f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert     * Returns an {@link AssetFileDescriptor} backed by the
6699d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * contents of the given {@link ByteArrayOutputStream}.
6700d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
6701f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor buildAssetFileDescriptor(ByteArrayOutputStream stream) {
6702d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        try {
6703d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            stream.flush();
6704d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6705d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final byte[] byteData = stream.toByteArray();
6706d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6707f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
6708f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    ParcelFileDescriptor.fromData(byteData, CONTACT_MEMORY_FILE_NAME),
6709f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    byteData.length);
6710d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        } catch (IOException e) {
6711ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            Log.w(TAG, "Problem writing stream into an ParcelFileDescriptor: " + e.toString());
6712ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            return null;
6713d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
6714d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
6715d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6716f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd) {
6717f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return makeAssetFileDescriptor(fd, AssetFileDescriptor.UNKNOWN_LENGTH);
6718f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
6719f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
6720f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd, long length) {
6721f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return fd != null ? new AssetFileDescriptor(fd, 0, length) : null;
6722f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
6723f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
6724d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
6725d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Output {@link RawContacts} matching the requested selection in the vCard
6726d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * format to the given {@link OutputStream}. This method returns silently if
6727d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * any errors encountered.
6728d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
6729fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen    private void outputRawContactsAsVCard(Uri uri, OutputStream stream,
6730fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            String selection, String[] selectionArgs) {
6731d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final Context context = this.getContext();
6732dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen        int vcardconfig = VCardConfig.VCARD_TYPE_DEFAULT;
6733fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen        if(uri.getBooleanQueryParameter(
6734fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                Contacts.QUERY_PARAMETER_VCARD_NO_PHOTO, false)) {
6735dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen            vcardconfig |= VCardConfig.FLAG_REFRAIN_IMAGE_EXPORT;
6736dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen        }
67377a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        final VCardComposer composer =
6738dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen                new VCardComposer(context, vcardconfig, false);
6739108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        Writer writer = null;
6740108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        try {
6741108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            writer = new BufferedWriter(new OutputStreamWriter(stream));
6742fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            if (!composer.init(uri, selection, selectionArgs, null)) {
6743108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                Log.w(TAG, "Failed to init VCardComposer");
6744108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                return;
6745108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            }
6746d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6747108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            while (!composer.isAfterLast()) {
6748108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                writer.write(composer.createOneEntry());
6749108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            }
6750108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        } catch (IOException e) {
6751108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            Log.e(TAG, "IOException: " + e);
6752108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        } finally {
6753108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            composer.terminate();
6754108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            if (writer != null) {
6755108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                try {
6756108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                    writer.close();
6757108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                } catch (IOException e) {
6758108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                    Log.w(TAG, "IOException during closing output stream: " + e);
6759108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                }
6760d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
6761d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
6762d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
6763b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
67644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
67654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
6766415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
6767415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov        waitForAccess(mReadAccessLatch);
6768415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
6769a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
67704f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
6771b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS:
6772be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return Contacts.CONTENT_TYPE;
67732d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill            case CONTACTS_LOOKUP:
6774b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_ID:
6775b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
677624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE:
6777b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_ITEM_TYPE;
6778f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD:
677942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD:
678024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD:
6781f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                return Contacts.CONTENT_VCARD_TYPE;
6782f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            case CONTACTS_ID_PHOTO:
6783f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_ID_DISPLAY_PHOTO:
6784f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_DISPLAY_PHOTO:
6785f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_ID_DISPLAY_PHOTO:
6786f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case RAW_CONTACTS_ID_DISPLAY_PHOTO:
6787f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case DISPLAY_PHOTO:
6788f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                return "image/jpeg";
6789b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS:
679024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS:
6791be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return RawContacts.CONTENT_TYPE;
6792b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS_ID:
679324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID:
6794b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return RawContacts.CONTENT_ITEM_TYPE;
6795f481f22a9323fe338672f99b88b26c5f0725cd42David Brown            case DATA:
679624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA:
6797f481f22a9323fe338672f99b88b26c5f0725cd42David Brown                return Data.CONTENT_TYPE;
6798508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
6799b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getDataMimeType(ContentUris.parseId(uri));
680048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES:
680148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_TYPE;
680248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
680348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_ITEM_TYPE;
68049005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov            case PHONE_LOOKUP:
68059005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov                return PhoneLookup.CONTENT_TYPE;
680648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS:
680748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_TYPE;
680848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
680948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_ITEM_TYPE;
681048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS:
681148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_TYPE;
681248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID:
681348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_ITEM_TYPE;
6814b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
6815b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_TYPE;
6816b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
6817b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_ITEM_TYPE;
6818b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case SETTINGS:
6819b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Settings.CONTENT_TYPE;
6820b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
6821b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_TYPE;
6822c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS:
6823c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SUGGEST_MIME_TYPE;
6824c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT:
6825c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
6826d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES:
6827d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_TYPE;
6828d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID:
6829d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_ITEM_TYPE;
683061efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov            default:
683161efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov                return mLegacyApiSupport.getType(uri);
68324f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
68334f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
68347e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
683509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    public String[] getDefaultProjection(Uri uri) {
683609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        final int match = sUriMatcher.match(uri);
683709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        switch (match) {
683809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS:
683909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP:
684009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_ID:
684109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
684209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
684324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE:
684409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsProjectionMap.getColumnNames();
684509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
68468727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov            case CONTACTS_ID_ENTITIES:
684724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_ENTITIES:
68488727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov                return sEntityProjectionMap.getColumnNames();
68498727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov
685009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_VCARD:
685109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_MULTI_VCARD:
685224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD:
685309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsVCardProjectionMap.getColumnNames();
685409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
685509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS:
685609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS_ID:
685724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS:
685824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID:
685909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sRawContactsProjectionMap.getColumnNames();
686009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
686109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DATA_ID:
686209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES:
686309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES_ID:
686409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS:
686509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS_ID:
686609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS:
686709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS_ID:
686824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA:
686909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDataProjectionMap.getColumnNames();
687009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
687109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONE_LOOKUP:
687209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sPhoneLookupProjectionMap.getColumnNames();
687309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
687409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
687509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
687609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sAggregationExceptionsProjectionMap.getColumnNames();
687709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
687809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case SETTINGS:
687909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sSettingsProjectionMap.getColumnNames();
688009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
688109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES:
688209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES_ID:
688309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDirectoryProjectionMap.getColumnNames();
688409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
688509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            default:
688609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return null;
688709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
688809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    }
688909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
6890f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private class StructuredNameLookupBuilder extends NameLookupBuilder {
6891f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6892f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public StructuredNameLookupBuilder(NameSplitter splitter) {
6893f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            super(splitter);
6894f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6895f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6896f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
6897f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
6898f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name) {
689978fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov            mDbHelper.insertNameLookup(rawContactId, dataId, lookupType, name);
6900f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6901f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6902f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
6903f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected String[] getCommonNicknameClusters(String normalizedName) {
6904d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov            return mCommonNicknameCache.getCommonNicknameClusters(normalizedName);
6905f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6906f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
6907f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
69082d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov    public void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
6909d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
6910d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + RawContacts.CONTACT_ID +
6911d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.RAW_CONTACTS +
6912d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " JOIN " + Tables.NAME_LOOKUP +
6913d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " ON(" + RawContactsColumns.CONCRETE_ID + "="
6914d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
6915d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE normalized_name GLOB '");
6916e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
6917916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE +
6918916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " IN(" + CONTACT_LOOKUP_NAME_TYPES + "))");
6919e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    }
6920e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
69219a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    public boolean isPhoneNumber(String filter) {
69229a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        boolean atLeastOneDigit = false;
69239a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        int len = filter.length();
69249a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        for (int i = 0; i < len; i++) {
69259a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            char c = filter.charAt(i);
69269a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            if (c >= '0' && c <= '9') {
69279a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                atLeastOneDigit = true;
69289a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            } else if (c != '*' && c != '#' && c != '+' && c != 'N' && c != '.' && c != ';'
69299a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                    && c != '-' && c != '(' && c != ')' && c != ' ') {
69309a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                return false;
69319a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            }
69329a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
69339a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return atLeastOneDigit;
69349a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
69359a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
69364a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /**
69377a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * Takes components of a name from the query parameters and returns a cursor with those
69387a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * components as well as all missing components.  There is no database activity involved
69397a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * in this so the call can be made on the UI thread.
69407a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     */
69417a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private Cursor completeName(Uri uri, String[] projection) {
69427a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        if (projection == null) {
69437a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            projection = sDataProjectionMap.getColumnNames();
69447a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
69457a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
69467a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        ContentValues values = new ContentValues();
6947f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        DataRowHandlerForStructuredName handler = (DataRowHandlerForStructuredName)
6948f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                getDataRowHandler(StructuredName.CONTENT_ITEM_TYPE);
69497a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
69507a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        copyQueryParamsToContentValues(values, uri,
69517a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.DISPLAY_NAME,
69527a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PREFIX,
69537a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.GIVEN_NAME,
69547a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.MIDDLE_NAME,
69557a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.FAMILY_NAME,
69567a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.SUFFIX,
69577a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_NAME,
69587a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_FAMILY_NAME,
69597a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_MIDDLE_NAME,
69607a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_GIVEN_NAME
69617a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        );
69627a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
69637a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        handler.fixStructuredNameComponents(values, values);
69647a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
69657a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
69667a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        Object[] row = new Object[projection.length];
69677a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
69687a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            row[i] = values.get(projection[i]);
69697a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
69707a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        cursor.addRow(row);
69717a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        return cursor;
69727a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
69737a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
69747a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private void copyQueryParamsToContentValues(ContentValues values, Uri uri, String... columns) {
69757a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (String column : columns) {
69767a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            String param = uri.getQueryParameter(column);
69777a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            if (param != null) {
69787a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                values.put(column, param);
69797a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
69807a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
69817a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
69827a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
69837a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
69847a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    /**
69854a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     * Inserts an argument at the beginning of the selection arg list.
69864a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     */
69874a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private String[] insertSelectionArg(String[] selectionArgs, String arg) {
6988b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
6989b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
6990b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
6991b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
6992b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
69934a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            newSelectionArgs[0] = arg;
69944a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length);
6995b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
6996b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
6997b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
6998caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
69995e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private String[] appendProjectionArg(String[] projection, String arg) {
70005e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        if (projection == null) {
70015e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            return null;
70025e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        }
70035e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        final int length = projection.length;
70045e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        String[] newProjection = new String[length + 1];
70055e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        System.arraycopy(projection, 0, newProjection, 0, length);
70065e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        newProjection[length] = arg;
70075e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        return newProjection;
70085e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    }
70095e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
7010caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    protected Account getDefaultAccount() {
7011caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        AccountManager accountManager = AccountManager.get(getContext());
7012caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        try {
70135f1f4a062ac34d75d2dbf586702cbeb121cf09caDmitri Plotnikov            Account[] accounts = accountManager.getAccountsByType(DEFAULT_ACCOUNT_TYPE);
7014caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            if (accounts != null && accounts.length > 0) {
7015caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov                return accounts[0];
7016caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            }
7017caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        } catch (Throwable e) {
70186f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov            Log.e(TAG, "Cannot determine the default account for contacts compatibility", e);
7019caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        }
70206f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov        return null;
7021caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    }
7022f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
702373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    /**
702443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * Returns true if the specified account type and data set is writable.
702573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     */
702643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    protected boolean isWritableAccountWithDataSet(String accountTypeAndDataSet) {
702743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        if (accountTypeAndDataSet == null) {
7028bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov            return true;
7029bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        }
7030bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov
703143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        Boolean writable = mAccountWritability.get(accountTypeAndDataSet);
703273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable != null) {
703373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            return writable;
703473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
703573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
7036627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        IContentService contentService = ContentResolver.getContentService();
7037627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
703843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // TODO(dsantoro): Need to update this logic to allow for sub-accounts.
7039627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (SyncAdapterType sync : contentService.getSyncAdapterTypes()) {
7040627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (ContactsContract.AUTHORITY.equals(sync.authority) &&
704143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        accountTypeAndDataSet.equals(sync.accountType)) {
704273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    writable = sync.supportsUploading();
704373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    break;
7044627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
7045627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
7046627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } catch (RemoteException e) {
7047627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            Log.e(TAG, "Could not acquire sync adapter types");
7048627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
704973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
705073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable == null) {
705173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            writable = false;
705273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
705373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
705443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        mAccountWritability.put(accountTypeAndDataSet, writable);
705573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        return writable;
7056627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
7057b4e61e064b59e8076df81b061add9fb358fd2ed9Dmitri Plotnikov
7058d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
7059f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static boolean readBooleanQueryParameter(Uri uri, String parameter,
7060f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean defaultValue) {
7061f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7062f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        // Manually parse the query, which is much faster than calling uri.getQueryParameter
7063f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
7064f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
7065f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
7066f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7067f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7068f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = query.indexOf(parameter);
7069f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (index == -1) {
7070f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
7071f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7072f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7073f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        index += parameter.length();
7074f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7075f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return !matchQueryParameter(query, index, "=0", false)
7076f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && !matchQueryParameter(query, index, "=false", true);
7077f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
7078f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7079f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private static boolean matchQueryParameter(String query, int index, String value,
7080f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean ignoreCase) {
7081f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int length = value.length();
7082f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return query.regionMatches(ignoreCase, index, value, 0, length)
7083f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && (query.length() == index + length || query.charAt(index + length) == '&');
7084f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
7085f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7086f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /**
7087f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * A fast re-implementation of {@link Uri#getQueryParameter}
7088f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     */
7089f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static String getQueryParameter(Uri uri, String parameter) {
7090f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
7091f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
7092f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return null;
7093f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7094f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7095f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int queryLength = query.length();
7096f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int parameterLength = parameter.length();
7097f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7098f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String value;
7099f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = 0;
7100f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        while (true) {
7101f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index = query.indexOf(parameter, index);
7102f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (index == -1) {
7103f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
71045fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            }
71055fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa
71065fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            // Should match against the whole parameter instead of its suffix.
71075fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            // e.g. The parameter "param" must not be found in "some_param=val".
71085fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            if (index > 0) {
71095fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                char prevChar = query.charAt(index - 1);
71105fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                if (prevChar != '?' && prevChar != '&') {
71115fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    // With "some_param=val1&param=val2", we should find second "param" occurrence.
71125fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    index += parameterLength;
71135fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    continue;
71145fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                }
7115f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
7116f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7117f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index += parameterLength;
7118f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7119f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (queryLength == index) {
7120f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
7121f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
7122f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7123f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (query.charAt(index) == '=') {
7124f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                index++;
7125f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                break;
7126f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
7127f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7128f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7129f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int ampIndex = query.indexOf('&', index);
7130f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (ampIndex == -1) {
7131f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index);
7132f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        } else {
7133f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index, ampIndex);
7134f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7135f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7136f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return Uri.decode(value);
7137f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
71385dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
71390dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    protected boolean isAggregationUpgradeNeeded() {
71400dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
71410dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            return false;
71420dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
71430dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
71440dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int version = Integer.parseInt(mDbHelper.getProperty(PROPERTY_AGGREGATION_ALGORITHM, "1"));
71450dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        return version < PROPERTY_AGGREGATION_ALGORITHM_VERSION;
71460dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
71470dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
7148bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void upgradeAggregationAlgorithmInBackground() {
71490dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // This upgrade will affect very few contacts, so it can be performed on the
71500dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // main thread during the initial boot after an OTA
71510dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
71520dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        Log.i(TAG, "Upgrading aggregation algorithm");
71530dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int count = 0;
71540dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        long start = SystemClock.currentThreadTimeMillis();
71550dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        try {
715649d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov            mDb = mDbHelper.getWritableDatabase();
71570dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.beginTransaction();
71580dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            Cursor cursor = mDb.query(true,
71590dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    Tables.RAW_CONTACTS + " r1 JOIN " + Tables.RAW_CONTACTS + " r2",
71600dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    new String[]{"r1." + RawContacts._ID},
71610dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    "r1." + RawContacts._ID + "!=r2." + RawContacts._ID +
71620dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.CONTACT_ID + "=r2." + RawContacts.CONTACT_ID +
71630dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.ACCOUNT_NAME + "=r2." + RawContacts.ACCOUNT_NAME +
716443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    " AND r1." + RawContacts.ACCOUNT_TYPE + "=r2." + RawContacts.ACCOUNT_TYPE +
716543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    " AND r1." + RawContacts.DATA_SET + "=r2." + RawContacts.DATA_SET,
71660dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    null, null, null, null, null);
71670dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            try {
71680dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                while (cursor.moveToNext()) {
71690dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    long rawContactId = cursor.getLong(0);
71700dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId,
71710dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                            RawContacts.AGGREGATION_MODE_DEFAULT, true);
71720dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    count++;
71730dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                }
71740dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            } finally {
71750dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                cursor.close();
71760dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            }
7177bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mContactAggregator.aggregateInTransaction(mTransactionContext, mDb);
7178bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            updateSearchIndexInTransaction();
71790dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.setTransactionSuccessful();
71800dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDbHelper.setProperty(PROPERTY_AGGREGATION_ALGORITHM,
71810dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    String.valueOf(PROPERTY_AGGREGATION_ALGORITHM_VERSION));
71820dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        } finally {
71830dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.endTransaction();
71840dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            long end = SystemClock.currentThreadTimeMillis();
71850dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            Log.i(TAG, "Aggregation algorithm upgraded for " + count
71860dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    + " contacts, in " + (end - start) + "ms");
71870dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
71880dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
71899a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
71909a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    /* Visible for testing */
71919a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    boolean isPhone() {
71929a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (!sIsPhoneInitialized) {
71939a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sIsPhone = new TelephonyManager(getContext()).isVoiceCapable();
71949a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sIsPhoneInitialized = true;
71959a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
71969a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return sIsPhone;
71979a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
719846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
719946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private boolean handleDataUsageFeedback(Uri uri) {
720046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final long currentTimeMillis = System.currentTimeMillis();
720146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String usageType = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
720246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] ids = uri.getLastPathSegment().trim().split(",");
720346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final ArrayList<Long> dataIds = new ArrayList<Long>();
720446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
720546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        for (String id : ids) {
720646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            dataIds.add(Long.valueOf(id));
720746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
720846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final boolean successful;
720946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (TextUtils.isEmpty(usageType)) {
721046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            Log.w(TAG, "Method for data usage feedback isn't specified. Ignoring.");
721146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            successful = false;
721246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } else {
721346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            successful = updateDataUsageStat(dataIds, usageType, currentTimeMillis) > 0;
721446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
721546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
721646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        // Handle old API. This doesn't affect the result of this entire method.
721746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] questionMarks = new String[ids.length];
721846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        Arrays.fill(questionMarks, "?");
721946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String where = Data._ID + " IN (" + TextUtils.join(",", questionMarks) + ")";
722046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final Cursor cursor = mDb.query(
7221ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                Views.DATA,
722246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                new String[] { Data.CONTACT_ID },
722346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                where, ids, null, null, null);
722446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        try {
722546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            while (cursor.moveToNext()) {
722646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mSelectionArgs1[0] = cursor.getString(0);
722746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                ContentValues values2 = new ContentValues();
722846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                values2.put(Contacts.LAST_TIME_CONTACTED, currentTimeMillis);
722946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDb.update(Tables.CONTACTS, values2, Contacts._ID + "=?", mSelectionArgs1);
723046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDb.execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
723146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDb.execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
723246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
723346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } finally {
723446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            cursor.close();
723546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
723646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
723746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        return successful;
723846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
723946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
724046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
724146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Update {@link Tables#DATA_USAGE_STAT}.
724246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     *
724346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * @return the number of rows affected.
724446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
7245f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa    @VisibleForTesting
7246f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa    /* package */ int updateDataUsageStat(
7247f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa            List<Long> dataIds, String type, long currentTimeMillis) {
724846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final int typeInt = sDataUsageTypeMap.get(type);
724946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String where = DataUsageStatColumns.DATA_ID + " =? AND "
725046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                + DataUsageStatColumns.USAGE_TYPE_INT + " =?";
725146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] columns =
725246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                new String[] { DataUsageStatColumns._ID, DataUsageStatColumns.TIMES_USED };
725346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final ContentValues values = new ContentValues();
725446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        for (Long dataId : dataIds) {
725546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            final String[] args = new String[] { dataId.toString(), String.valueOf(typeInt) };
725646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            mDb.beginTransaction();
725746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            try {
725846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                final Cursor cursor = mDb.query(Tables.DATA_USAGE_STAT, columns, where, args,
725946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        null, null, null);
726046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                try {
726146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (cursor.getCount() > 0) {
726246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        if (!cursor.moveToFirst()) {
726346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            Log.e(TAG,
726446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    "moveToFirst() failed while getAccount() returned non-zero.");
726546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        } else {
726646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.clear();
726746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.put(DataUsageStatColumns.TIMES_USED, cursor.getInt(1) + 1);
726846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.put(DataUsageStatColumns.LAST_TIME_USED, currentTimeMillis);
726946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            mDb.update(Tables.DATA_USAGE_STAT, values,
727046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    DataUsageStatColumns._ID + " =?",
727146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    new String[] { cursor.getString(0) });
727246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        }
727346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    } else {
727446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.clear();
727546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.DATA_ID, dataId);
727646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.USAGE_TYPE_INT, typeInt);
727746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.TIMES_USED, 1);
727846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.LAST_TIME_USED, currentTimeMillis);
727946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        mDb.insert(Tables.DATA_USAGE_STAT, null, values);
728046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    }
728146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    mDb.setTransactionSuccessful();
728246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                } finally {
728346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    cursor.close();
728446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
728546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            } finally {
728646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDb.endTransaction();
728746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
728846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
728946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
729046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        return dataIds.size();
729146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
729246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
729346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
729446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Returns a sort order String for promoting data rows (email addresses, phone numbers, etc.)
729546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * associated with a primary account. The primary account should be supplied from applications
729646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * with {@link ContactsContract#PRIMARY_ACCOUNT_NAME} and
729746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * {@link ContactsContract#PRIMARY_ACCOUNT_TYPE}. Null will be returned when the primary
729846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * account isn't available.
729946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
730046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private String getAccountPromotionSortOrder(Uri uri) {
730146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String primaryAccountName =
730246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME);
730346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String primaryAccountType =
730446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE);
730546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
730646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        // Data rows associated with primary account should be promoted.
730746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (!TextUtils.isEmpty(primaryAccountName)) {
730846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            StringBuilder sb = new StringBuilder();
730946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            sb.append("(CASE WHEN " + RawContacts.ACCOUNT_NAME + "=");
731046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            DatabaseUtils.appendEscapedSQLString(sb, primaryAccountName);
731146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            if (!TextUtils.isEmpty(primaryAccountType)) {
731246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                sb.append(" AND " + RawContacts.ACCOUNT_TYPE + "=");
731346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DatabaseUtils.appendEscapedSQLString(sb, primaryAccountType);
731446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
731546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            sb.append(" THEN 0 ELSE 1 END)");
731646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            return sb.toString();
731746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } else {
731846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            return null;
731946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
732046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
73214f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
7322