ContactsProvider2.java revision 45ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ec
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;
145f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport java.io.FileDescriptor;
146f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport java.io.FileInputStream;
147b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport java.io.FileNotFoundException;
148f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport java.io.FileOutputStream;
149d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.IOException;
150d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.OutputStream;
151108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawaimport java.io.OutputStreamWriter;
152108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawaimport java.io.Writer;
15342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.text.SimpleDateFormat;
1547e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport java.util.ArrayList;
15546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawaimport java.util.Arrays;
1565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.Collections;
15742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.util.Date;
158b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport java.util.HashMap;
1590e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.HashSet;
1605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.List;
161622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkeyimport java.util.Locale;
162b5a4add17815167d20a90645779df34cdf45280dFred Quintanaimport java.util.Map;
1630e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.Set;
164ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikovimport java.util.concurrent.CountDownLatch;
1654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/**
1674f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Contacts content provider. The contract between this provider and applications
1684f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * is defined in {@link ContactsContract}.
1694f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
1705b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanapublic class ContactsProvider2 extends SQLiteContentProvider implements OnAccountsUpdateListener {
171caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
172bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final String TAG = "ContactsProvider";
173bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov
174bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE);
1754f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
17615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_INITIALIZE = 0;
17715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_OPEN_WRITE_ACCESS = 1;
17815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS = 2;
17915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_ACCOUNTS = 3;
18015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_LOCALE = 4;
18115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM = 5;
18205e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_SEARCH_INDEX = 6;
18305e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_PROVIDER_STATUS = 7;
18405e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_DIRECTORIES = 8;
18505e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_CHANGE_LOCALE = 9;
186f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int BACKGROUND_TASK_CLEANUP_PHOTOS = 10;
187619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Default for the maximum number of returned aggregation suggestions. */
1893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DEFAULT_MAX_SUGGESTIONS = 5;
1903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /** Limit for the maximum number of social stream items to store under a raw contact. */
1923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int MAX_STREAM_ITEMS_PER_RAW_CONTACT = 5;
1933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
194f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /** Rate limit (in ms) for photo cleanup.  Do it at most once per day. */
195f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int PHOTO_CLEANUP_RATE_LIMIT = 24 * 60 * 60 * 1000;
196f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1973d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /**
198b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov     * Property key for the legacy contact import version. The need for a version
1993d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * as opposed to a boolean flag is that if we discover bugs in the contact import process,
2003d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * we can trigger re-import by incrementing the import version.
2013d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     */
202b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final String PROPERTY_CONTACTS_IMPORTED = "contacts_imported_v1";
203b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final int PROPERTY_CONTACTS_IMPORT_VERSION = 1;
20451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    private static final String PREF_LOCALE = "locale";
2053d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2060dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static final String PROPERTY_AGGREGATION_ALGORITHM = "aggregation_v2";
2070dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static final int PROPERTY_AGGREGATION_ALGORITHM_VERSION = 2;
2080dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
2090e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff    private static final String AGGREGATE_CONTACTS = "sync.contacts.aggregate";
2100e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff
211a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
2124f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2132f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    /**
2142f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     * Used to insert a column into strequent results, which enables SQL to sort the list using
2152f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     * the total times contacted. See also {@link #sStrequentFrequentProjectionMap}.
2162f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     */
2172f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    private static final String TIMES_USED_SORT_COLUMN = "times_used_sort";
2185e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
219d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final String STREQUENT_ORDER_BY = Contacts.STARRED + " DESC, "
2202f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            + TIMES_USED_SORT_COLUMN + " DESC, "
2219b43551f1ce33b79141772737a262ce609bd0cebMegha Joshi            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
222d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final String STREQUENT_LIMIT =
223d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            "(SELECT COUNT(1) FROM " + Tables.CONTACTS + " WHERE "
224d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            + Contacts.STARRED + "=1) + 25";
225d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
22645ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa    private static final String FREQUENT_ORDER_BY = DataUsageStatColumns.TIMES_USED + " DESC,"
22745ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
22845ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa
2296e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_CONTACTS_TABLE =
2309b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.CONTACTS + " SET " + Contacts.TIMES_CONTACTED + "=" +
2319b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + Contacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
2329b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + Contacts.TIMES_CONTACTED + " + 1) END WHERE " + Contacts._ID + "=?";
2339b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
2346e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE =
2359b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.RAW_CONTACTS + " SET " + RawContacts.TIMES_CONTACTED + "=" +
2369b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + RawContacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
2379b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + RawContacts.TIMES_CONTACTED + " + 1) END WHERE " + RawContacts.CONTACT_ID + "=?";
2389b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
239de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa    /* package */ static final String PHONEBOOK_COLLATOR_NAME = "PHONEBOOK";
240de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa
2413716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    // Regex for splitting query strings - we split on any group of non-alphanumeric characters,
2423716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    // excluding the @ symbol.
2433716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    /* package */ static final String QUERY_TOKENIZER_REGEX = "[^\\w@]+";
2443716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
245d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS = 1000;
246d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS_ID = 1001;
2475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP = 1002;
2485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID = 1003;
249a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_DATA = 1004;
2505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_FILTER = 1005;
2515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT = 1006;
2525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT_FILTER = 1007;
2535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_GROUP = 1008;
254a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_PHOTO = 1009;
255f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_ID_DISPLAY_PHOTO = 1010;
256f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_LOOKUP_DISPLAY_PHOTO = 1011;
257f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_LOOKUP_ID_DISPLAY_PHOTO = 1012;
258f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_AS_VCARD = 1013;
259f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_AS_MULTI_VCARD = 1014;
260f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_LOOKUP_DATA = 1015;
261f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_LOOKUP_ID_DATA = 1016;
262f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_ID_ENTITIES = 1017;
263f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_LOOKUP_ENTITIES = 1018;
264f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_LOOKUP_ID_ENTITIES = 1019;
265f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_ID_STREAM_ITEMS = 1020;
266f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_LOOKUP_STREAM_ITEMS = 1021;
267f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_LOOKUP_ID_STREAM_ITEMS = 1022;
26845ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa    private static final int CONTACTS_FREQUENT = 1023;
2694f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2705ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS = 2002;
2715ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_ID = 2003;
2725ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_DATA = 2004;
27346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITY_ID = 2005;
274f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int RAW_CONTACTS_ID_DISPLAY_PHOTO = 2006;
275f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int RAW_CONTACTS_ID_STREAM_ITEMS = 2007;
2764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2776bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
2786bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
279ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
28048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_ID = 3003;
28148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_FILTER = 3004;
28248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS = 3005;
28348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_ID = 3006;
28448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_LOOKUP = 3007;
28548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_FILTER = 3008;
28648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS = 3009;
28748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS_ID = 3010;
288a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2896bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
2906bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
291b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
292b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
293b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
29482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES = 7000;
29582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES_ID = 7001;
2961f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
29731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
29831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
299eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final int SETTINGS = 9000;
300eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
301ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
302ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
303ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
304ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
30535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
306b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private static final int SYNCSTATE_ID = 11001;
30735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
308c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SUGGESTIONS = 12001;
309c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SHORTCUT = 12002;
310c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
3111b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS = 14000;
3121b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_WITH_PHONES = 14001;
3131b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_FAVORITES = 14002;
3141b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_GROUP_NAME = 14003;
3151b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
31646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITIES = 15001;
31746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
31809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private static final int PROVIDER_STATUS = 16001;
31909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
320d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES = 17001;
321d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES_ID = 17002;
322d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3237a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private static final int COMPLETE_NAME = 18000;
3247a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
32524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE = 19000;
32624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_ENTITIES = 19001;
32724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_DATA = 19002;
32824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_DATA_ID = 19003;
32924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_AS_VCARD = 19004;
33024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS = 19005;
33124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID = 19006;
33224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID_DATA = 19007;
33324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID_ENTITIES = 19008;
33424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
33546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final int DATA_USAGE_FEEDBACK_ID = 20001;
33646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
3373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS = 21000;
3383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_PHOTOS = 21001;
3393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID = 21002;
3403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID_PHOTOS = 21003;
3413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID_PHOTOS_ID = 21004;
3423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_LIMIT = 21005;
3433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
344f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int DISPLAY_PHOTO = 22000;
345f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int PHOTO_DIMENSIONS = 22001;
346f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
347dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID =
348dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
349dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME
350dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
351dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE
352dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_TYPE
353dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " AND " + Groups.FAVORITES + " != 0";
354dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
355dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID =
356dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
357dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
358dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
359dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
360dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AND "
361dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + Groups.AUTO_ADD + " != 0";
362dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
363dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String[] PROJECTION_GROUP_ID
364dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            = new String[]{Tables.GROUPS + "." + Groups._ID};
365dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
366dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_GROUPMEMBERSHIP_DATA = DataColumns.MIMETYPE_ID + "=? "
367dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.GROUP_ROW_ID + "=? "
368dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.RAW_CONTACT_ID + "=?";
369dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
370dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_STARRED_FROM_RAW_CONTACTS =
371dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            "SELECT " + RawContacts.STARRED
372dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " FROM " + Tables.RAW_CONTACTS + " WHERE " + RawContacts._ID + "=?";
373dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
374e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    public class AddressBookCursor extends CursorWrapper implements CrossProcessCursor {
375e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        private final CrossProcessCursor mCursor;
376e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        private final Bundle mBundle;
377e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
378e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public AddressBookCursor(CrossProcessCursor cursor, String[] titles, int[] counts) {
379e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            super(cursor);
380e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mCursor = cursor;
381e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mBundle = new Bundle();
382e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mBundle.putStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES, titles);
383e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mBundle.putIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS, counts);
384e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
385e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
386e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        @Override
387e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public Bundle getExtras() {
388e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return mBundle;
389e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
390e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
391e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        @Override
392e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public void fillWindow(int pos, CursorWindow window) {
393e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mCursor.fillWindow(pos, window);
394e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
395e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
396e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        @Override
397e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public CursorWindow getWindow() {
398e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return mCursor.getWindow();
399e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
400e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
401e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        @Override
402e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public boolean onMove(int oldPosition, int newPosition) {
403e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return mCursor.onMove(oldPosition, newPosition);
404e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
405e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    }
406e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
407d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private interface DataContactsQuery {
408f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        public static final String TABLE = "data "
409f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) "
410f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN contacts ON (raw_contacts.contact_id = contacts._id)";
41167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
41267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
4136cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
4146802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            RawContactsColumns.CONCRETE_ACCOUNT_TYPE,
4156802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            RawContactsColumns.CONCRETE_ACCOUNT_NAME,
4163cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
417f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            ContactsColumns.CONCRETE_ID
418ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
419ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
420d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
4216802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        public static final int ACCOUNT_TYPE = 1;
4226802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        public static final int ACCOUNT_NAME = 2;
4236802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        public static final int DATA_ID = 3;
4246802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        public static final int CONTACT_ID = 4;
425ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
4261f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
427f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov    interface RawContactsQuery {
42819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String TABLE = Tables.RAW_CONTACTS;
42919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
43019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String[] COLUMNS = new String[] {
431ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.DELETED,
432ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
433ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
43419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        };
43519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
43619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int DELETED = 0;
437ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_TYPE = 1;
438ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_NAME = 2;
43919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    }
44019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
441c76cdd0723b99f478c9ba5329d14a971cd8dfb3dCostin Manolache    public static final String DEFAULT_ACCOUNT_TYPE = "com.google";
442caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
44371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    /** Sql where statement for filtering on groups. */
44471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private static final String CONTACTS_IN_GROUP_SELECT =
44571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov            Contacts._ID + " IN "
44671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + "(SELECT " + RawContacts.CONTACT_ID
44771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " FROM " + Tables.RAW_CONTACTS
44871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + " IN "
44971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
45071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " FROM " + Tables.DATA_JOIN_MIMETYPES
45171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " WHERE " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
45271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "' AND " + GroupMembership.GROUP_ROW_ID + "="
45371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "(SELECT " + Tables.GROUPS + "." + Groups._ID
45471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " FROM " + Tables.GROUPS
45571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " WHERE " + Groups.TITLE + "=?)))";
45671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov
457a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating DIRTY flag on multiple raw contacts */
458a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_DIRTY_SQL =
459a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
460a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.DIRTY + "=1" +
461a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
462a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
463a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating VERSION on multiple raw contacts */
464a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_VERSION_SQL =
465a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
466a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.VERSION + " = " + RawContacts.VERSION + " + 1" +
467a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
468a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
469c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Current contacts - those contacted within the last 3 days (in seconds)
470c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_CURRENT = 3 * 24 * 60 * 60;
471c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
472c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Recent contacts - those contacted within the last 30 days (in seconds)
473c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_RECENT = 30 * 24 * 60 * 60;
474c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
475f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa    private static final String TIME_SINCE_LAST_USED =
476f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa            "(strftime('%s', 'now') - " + DataUsageStatColumns.LAST_TIME_USED + "/1000)";
477f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa
478c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    /*
479c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * Sorting order for email address suggestions: first starred, then the rest.
4802262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * second in_visible_group, then the rest.
4812262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * Within the four (starred/unstarred, in_visible_group/not-in_visible_group) groups
4822262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * - three buckets: very recently contacted, then fairly
483c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * recently contacted, then the rest.  Within each of the bucket - descending count
48446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * of times contacted (both for data row and for contact row). If all else fails, alphabetical.
48546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * (Super)primary email address is returned before other addresses for the same contact.
486c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     */
487c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final String EMAIL_FILTER_SORT_ORDER =
4882262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa        Contacts.STARRED + " DESC, "
4892262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa        + Contacts.IN_VISIBLE_GROUP + " DESC, "
490f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa        + "(CASE WHEN " + TIME_SINCE_LAST_USED + " < " + EMAIL_FILTER_CURRENT
49146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " THEN 0 "
492f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa                + " WHEN " + TIME_SINCE_LAST_USED + " < " + EMAIL_FILTER_RECENT
49346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " THEN 1 "
49446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " ELSE 2 END), "
49546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + DataUsageStatColumns.TIMES_USED + " DESC, "
49646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + Contacts.DISPLAY_NAME + ", "
49746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + Data.CONTACT_ID + ", "
498c591cc2ffecdd0038f787a133606752752294c13Daisuke Miyakawa        + Data.IS_SUPER_PRIMARY + " DESC, "
499c591cc2ffecdd0038f787a133606752752294c13Daisuke Miyakawa        + Data.IS_PRIMARY + " DESC";
50046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
50146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /** Currently same as {@link #EMAIL_FILTER_SORT_ORDER} */
50246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final String PHONE_FILTER_SORT_ORDER = EMAIL_FILTER_SORT_ORDER;
503c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
504916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Name lookup types used for contact filtering */
505916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private static final String CONTACT_LOOKUP_NAME_TYPES =
506916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NAME_COLLATION_KEY + "," +
507916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.EMAIL_BASED_NICKNAME + "," +
50892ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov            NameLookupType.NICKNAME;
509916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
510f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    /**
511f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * If any of these columns are used in a Data projection, there is no point in
512f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * using the DISTINCT keyword, which can negatively affect performance.
513f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     */
514f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    private static final String[] DISTINCT_DATA_PROHIBITING_COLUMNS = {
515f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data._ID,
516f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.RAW_CONTACT_ID,
517f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.NAME_RAW_CONTACT_ID,
518f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_NAME,
519f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_TYPE,
520f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.DIRTY,
521f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.NAME_VERIFIED,
522f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.SOURCE_ID,
523f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.VERSION,
524f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    };
525916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
526f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsColumns = ProjectionMap.builder()
527f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CUSTOM_RINGTONE)
528f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME)
529f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_ALTERNATIVE)
530f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_SOURCE)
531f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.IN_VISIBLE_GROUP)
532f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LAST_TIME_CONTACTED)
533f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LOOKUP_KEY)
534f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME)
535f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME_STYLE)
536f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHOTO_ID)
537f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            .add(Contacts.PHOTO_FILE_ID)
5383d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_URI)
5393d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_THUMBNAIL_URI)
540f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SEND_TO_VOICEMAIL)
541f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_ALTERNATIVE)
542f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_PRIMARY)
543f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.STARRED)
544f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.TIMES_CONTACTED)
545cf832869bcf91b8037d8b7f510a3a213b30764a3Dmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
546f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
547f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
548f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsPresenceColumns = ProjectionMap.builder()
549f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
550f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE)
551f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
552f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
553f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
554f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
555f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
556f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
557f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
558f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
559f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
560f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
561f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
562f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
563f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
564f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
565f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSnippetColumns = ProjectionMap.builder()
56603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET)
567f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
568f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
569f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactColumns = ProjectionMap.builder()
570f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_NAME)
571f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_TYPE)
572f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DIRTY)
573f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.NAME_VERIFIED)
574f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SOURCE_ID)
575f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.VERSION)
576f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
577f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
578f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactSyncColumns = ProjectionMap.builder()
579f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC1)
580f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC2)
581f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC3)
582f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC4)
583f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
584f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
585f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataColumns = ProjectionMap.builder()
586f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA1)
587f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA2)
588f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA3)
589f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA4)
590f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA5)
591f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA6)
592f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA7)
593f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA8)
594f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA9)
595f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA10)
596f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA11)
597f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA12)
598f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA13)
599f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA14)
600f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA15)
601f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA_VERSION)
602f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_PRIMARY)
603f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_SUPER_PRIMARY)
604f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.MIMETYPE)
605f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RES_PACKAGE)
606f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC1)
607f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC2)
608f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC3)
609f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC4)
610f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(GroupMembership.GROUP_SOURCE_ID)
611f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
612f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
613f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactPresenceColumns = ProjectionMap.builder()
614f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
615f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.PRESENCE)
616f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
617f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.CHAT_CAPABILITY)
618f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
619f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
620f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
621f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
622f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
623f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
624f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
625f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
626f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
627f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
628f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
629f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
630f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataPresenceColumns = ProjectionMap.builder()
631f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.PRESENCE, Tables.PRESENCE + "." + StatusUpdates.PRESENCE)
632f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CHAT_CAPABILITY, Tables.PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
633f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS, StatusUpdatesColumns.CONCRETE_STATUS)
634f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_TIMESTAMP, StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
635f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_RES_PACKAGE, StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
636f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_LABEL, StatusUpdatesColumns.CONCRETE_STATUS_LABEL)
637f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_ICON, StatusUpdatesColumns.CONCRETE_STATUS_ICON)
638f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
639f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
640038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    /** Contains just BaseColumns._COUNT */
641f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sCountProjectionMap = ProjectionMap.builder()
642f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(BaseColumns._COUNT, "COUNT(*)")
643f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
644f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
645e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    /** Contains just the contacts columns */
646f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionMap = ProjectionMap.builder()
647f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts._ID)
648f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
649f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.NAME_RAW_CONTACT_ID)
65024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(Contacts.IS_USER_PROFILE)
651f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
652f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsPresenceColumns)
653f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
654f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
655916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Contains just the contacts columns */
656f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionWithSnippetMap = ProjectionMap.builder()
657f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
658f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sSnippetColumns)
659f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
660916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
6615e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    /** Used for pushing starred contacts to the top of a times contacted list **/
662f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentStarredProjectionMap = ProjectionMap.builder()
663f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
6642f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, String.valueOf(Long.MAX_VALUE))
665f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
666f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
667f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentFrequentProjectionMap = ProjectionMap.builder()
668f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
6692f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, "SUM(" + DataUsageStatColumns.CONCRETE_TIMES_USED + ")")
670f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
671f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6724928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    /**
6734928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * Used for Strequent Uri with {@link ContactsContract#STREQUENT_PHONE_ONLY}, which allows
6744928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * users to obtain part of Data columns. Right now Starred part just returns NULL for
6754928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * those data columns (frequent part should return real ones in data table).
6764928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     **/
6774928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    private static final ProjectionMap sStrequentPhoneOnlyStarredProjectionMap
6784928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            = ProjectionMap.builder()
6794928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .addAll(sContactsProjectionMap)
6804928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, String.valueOf(Long.MAX_VALUE))
6814928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.NUMBER, "NULL")
6824928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.TYPE, "NULL")
6834928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.LABEL, "NULL")
6844928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .build();
6854928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
6864928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    /**
6874928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * Used for Strequent Uri with {@link ContactsContract#STREQUENT_PHONE_ONLY}, which allows
6884928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * users to obtain part of Data columns. We hard-code {@link Contacts#IS_USER_PROFILE} to NULL,
6894928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * because sContactsProjectionMap specifies a field that doesn't exist in the view behind the
6904928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * query that uses this projection map.
6914928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     **/
6924928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    private static final ProjectionMap sStrequentPhoneOnlyFrequentProjectionMap
6934928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            = ProjectionMap.builder()
6944928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .addAll(sContactsProjectionMap)
6954928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, DataUsageStatColumns.CONCRETE_TIMES_USED)
6964928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.NUMBER)
6974928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.TYPE)
6984928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.LABEL)
6994928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Contacts.IS_USER_PROFILE, "NULL")
7004928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .build();
7014928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
702f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    /** Contains just the contacts vCard columns */
703f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsVCardProjectionMap = ProjectionMap.builder()
704fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            .add(Contacts._ID)
705f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.DISPLAY_NAME, Contacts.DISPLAY_NAME + " || '.vcf'")
706f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.SIZE, "NULL")
707f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
708f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
709ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    /** Contains just the raw contacts columns */
710f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactsProjectionMap = ProjectionMap.builder()
711f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
712f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
713f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
714f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_PRIMARY)
715f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_ALTERNATIVE)
716f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_SOURCE)
717f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME)
718f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME_STYLE)
719f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_PRIMARY)
720f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_ALTERNATIVE)
721f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.TIMES_CONTACTED)
722f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.LAST_TIME_CONTACTED)
723f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CUSTOM_RINGTONE)
724f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SEND_TO_VOICEMAIL)
725f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
726f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.AGGREGATION_MODE)
72724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
728f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
729f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
730f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
731f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
732a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the raw entity view*/
733f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawEntityProjectionMap = ProjectionMap.builder()
734f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
735f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
736f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.Entity.DATA_ID)
737f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
738f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
73924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
740f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
741f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
742f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
743f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
744f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
745a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the contact entity view*/
746f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sEntityProjectionMap = ProjectionMap.builder()
747f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity._ID)
748f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.CONTACT_ID)
749f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.RAW_CONTACT_ID)
750f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DATA_ID)
751f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.NAME_RAW_CONTACT_ID)
752f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DELETED)
75324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(Contacts.IS_USER_PROFILE)
754f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
755f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
756f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
757f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
758f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
759f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
760f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
761f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
7624a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /** Contains columns from the data view */
763f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataProjectionMap = ProjectionMap.builder()
764f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID)
765f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RAW_CONTACT_ID)
766f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CONTACT_ID)
767f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.NAME_RAW_CONTACT_ID)
76824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
769f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
770f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
771f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
772f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
773f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
774f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
775f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
7765e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /** Contains columns from the data view */
777f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDistinctDataProjectionMap = ProjectionMap.builder()
778f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID, "MIN(" + Data._ID + ")")
779f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
78024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
781f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
782f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
783f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
784f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
785f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
786f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
7879261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
788f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sPhoneLookupProjectionMap = ProjectionMap.builder()
789f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup._ID, "contacts_view." + Contacts._ID)
790f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LOOKUP_KEY, "contacts_view." + Contacts.LOOKUP_KEY)
791f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.DISPLAY_NAME, "contacts_view." + Contacts.DISPLAY_NAME)
792f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LAST_TIME_CONTACTED, "contacts_view." + Contacts.LAST_TIME_CONTACTED)
793f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TIMES_CONTACTED, "contacts_view." + Contacts.TIMES_CONTACTED)
794f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.STARRED, "contacts_view." + Contacts.STARRED)
795f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.IN_VISIBLE_GROUP, "contacts_view." + Contacts.IN_VISIBLE_GROUP)
796f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.PHOTO_ID, "contacts_view." + Contacts.PHOTO_ID)
7973d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_URI, "contacts_view." + Contacts.PHOTO_URI)
7983d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_THUMBNAIL_URI, "contacts_view." + Contacts.PHOTO_THUMBNAIL_URI)
799f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.CUSTOM_RINGTONE, "contacts_view." + Contacts.CUSTOM_RINGTONE)
800f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.HAS_PHONE_NUMBER, "contacts_view." + Contacts.HAS_PHONE_NUMBER)
801f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.SEND_TO_VOICEMAIL, "contacts_view." + Contacts.SEND_TO_VOICEMAIL)
802f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.NUMBER, Phone.NUMBER)
803f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TYPE, Phone.TYPE)
804f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LABEL, Phone.LABEL)
8052530512f639c4979fd7371c7dd25dd67e8118124Bai Tao            .add(PhoneLookup.NORMALIZED_NUMBER, Phone.NORMALIZED_NUMBER)
806f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
807f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
808ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
809f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsProjectionMap = ProjectionMap.builder()
810f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups._ID)
811f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_NAME)
812f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_TYPE)
813f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SOURCE_ID)
814f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DIRTY)
815f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.VERSION)
816f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.RES_PACKAGE)
817f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE)
818f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE_RES)
819f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.GROUP_VISIBLE)
820f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYSTEM_ID)
821f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DELETED)
822f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.NOTES)
8231cdfc9dacc136e99d3c0bc5b4212bc3c973be337Daniel Lehmann            .add(Groups.ACTION)
8241cdfc9dacc136e99d3c0bc5b4212bc3c973be337Daniel Lehmann            .add(Groups.ACTION_URI)
825f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SHOULD_SYNC)
826f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.FAVORITES)
827f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.AUTO_ADD)
828c039cfb78c40730483fd71178df63ada5826a315Dmitri Plotnikov            .add(Groups.GROUP_IS_READ_ONLY)
829f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC1)
830f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC2)
831f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC3)
832f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC4)
833f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
834f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
835ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains {@link Groups} columns along with summary details */
836f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsSummaryProjectionMap = ProjectionMap.builder()
837f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sGroupsProjectionMap)
838f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_COUNT,
839f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
840f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS
841f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " WHERE " + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP
842f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Clauses.BELONGS_TO_GROUP
843f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ")")
844f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_WITH_PHONES,
845f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
846f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS
847f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " WHERE " + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP
848f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Clauses.BELONGS_TO_GROUP
849f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Contacts.HAS_PHONE_NUMBER + ")")
850f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
851f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
852373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains the agg_exceptions columns */
853f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sAggregationExceptionsProjectionMap = ProjectionMap.builder()
854f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id")
855f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.TYPE)
856f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID1)
857f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID2)
858f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
859f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
860eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    /** Contains the agg_exceptions columns */
861f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSettingsProjectionMap = ProjectionMap.builder()
862f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_NAME)
863f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_TYPE)
864f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_VISIBLE)
865f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.SHOULD_SYNC)
866f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ANY_UNSYNCED,
867f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN MIN(" + Settings.SHOULD_SYNC
868f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                        + ",(SELECT "
869f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + "(CASE WHEN MIN(" + Groups.SHOULD_SYNC + ") IS NULL"
870f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " THEN 1"
871f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " ELSE MIN(" + Groups.SHOULD_SYNC + ")"
872f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " END)"
873f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.GROUPS
874f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
875f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_NAME
876f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
877f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_TYPE + "))=0"
878f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN 1"
879f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE 0"
880f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " END)")
881f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_COUNT,
882f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
883f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
884f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
885f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
886f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
887f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
888f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_WITH_PHONES,
889f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
890f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
891f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
892f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + Contacts.HAS_PHONE_NUMBER
893f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
894f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
895f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
896f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
897f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
89882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Contains StatusUpdates columns */
899f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStatusUpdatesProjectionMap = ProjectionMap.builder()
900f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PresenceColumns.RAW_CONTACT_ID)
901f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.DATA_ID, DataColumns.CONCRETE_ID)
902f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_ACCOUNT)
903f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_HANDLE)
904f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PROTOCOL)
905f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // We cannot allow a null in the custom protocol field, because SQLite3 does not
906f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // properly enforce uniqueness of null values
907f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CUSTOM_PROTOCOL,
908f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN " + StatusUpdates.CUSTOM_PROTOCOL + "=''"
909f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN NULL"
910f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE " + StatusUpdates.CUSTOM_PROTOCOL + " END)")
911f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PRESENCE)
912f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CHAT_CAPABILITY)
913f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS)
914f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_TIMESTAMP)
915f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_RES_PACKAGE)
916f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_ICON)
917f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_LABEL)
918f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
919f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
9203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /** Contains StreamItems columns */
9213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final ProjectionMap sStreamItemsProjectionMap = ProjectionMap.builder()
9223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems._ID, StreamItemsColumns.CONCRETE_ID)
9233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(RawContacts.CONTACT_ID)
9243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RAW_CONTACT_ID)
9253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_PACKAGE)
9263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_ICON)
9273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_LABEL)
9283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.TEXT)
9293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.TIMESTAMP)
9303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.COMMENTS)
9313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.ACTION)
9323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.ACTION_URI)
9333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .build();
9343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
9353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final ProjectionMap sStreamItemPhotosProjectionMap = ProjectionMap.builder()
9363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos._ID, StreamItemPhotosColumns.CONCRETE_ID)
9373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RAW_CONTACT_ID)
9383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.STREAM_ITEM_ID)
9393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.SORT_INDEX)
9406802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            .add(StreamItemPhotos.PHOTO_FILE_ID)
9416802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            .add(StreamItemPhotos.PHOTO_URI,
9426802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    "'" + DisplayPhoto.CONTENT_URI + "'||'/'||" + StreamItemPhotos.PHOTO_FILE_ID)
9433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.ACTION, StreamItemPhotosColumns.CONCRETE_ACTION)
9443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.ACTION_URI, StreamItemPhotosColumns.CONCRETE_ACTION_URI)
9451dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.HEIGHT)
9461dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.WIDTH)
9471dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.FILESIZE)
9483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .build();
9493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
9501b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    /** Contains Live Folders columns */
951f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sLiveFoldersProjectionMap = ProjectionMap.builder()
952f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders._ID, Contacts._ID)
953f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders.NAME, Contacts.DISPLAY_NAME)
954f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // TODO: Put contact photo back when we have a way to display a default icon
955f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // for contacts without a photo
956f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // .add(LiveFolders.ICON_BITMAP, Photos.DATA)
957f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
958f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
959d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /** Contains {@link Directory} columns */
960f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDirectoryProjectionMap = ProjectionMap.builder()
961f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory._ID)
962f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.PACKAGE_NAME)
963f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.TYPE_RESOURCE_ID)
964f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DISPLAY_NAME)
965f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DIRECTORY_AUTHORITY)
966f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_TYPE)
967f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_NAME)
968f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.EXPORT_SUPPORT)
969778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.SHORTCUT_SUPPORT)
970778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.PHOTO_SUPPORT)
971f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
9727e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
9739705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    // where clause to update the status_updates table
9749705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private static final String WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE =
9759705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdatesColumns.DATA_ID + " IN (SELECT Distinct " + StatusUpdates.DATA_ID +
9769705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " FROM " + Tables.STATUS_UPDATES + " LEFT OUTER JOIN " + Tables.PRESENCE +
9779705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " ON " + StatusUpdatesColumns.DATA_ID + " = " + StatusUpdates.DATA_ID + " WHERE ";
9789705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
9792526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private static final String[] EMPTY_STRING_ARRAY = new String[0];
9802526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
981bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
982bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Notification ID for failure to import contacts.
983bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
984bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private static final int LEGACY_IMPORT_FAILED_NOTIFICATION = 1;
98551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
98603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_START_MATCH = "[";
98703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_END_MATCH = "]";
98803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_ELLIPSIS = "...";
98903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final int DEFAULT_SNIPPET_ARG_MAX_TOKENS = -10;
99003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
9919a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    private boolean sIsPhoneInitialized;
9929a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    private boolean sIsPhone;
9939a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
994f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private StringBuilder mSb = new StringBuilder();
9951129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs1 = new String[1];
9961129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs2 = new String[2];
9972526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private ArrayList<String> mSelectionArgs = Lists.newArrayList();
9982526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
999f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private Account mAccount;
1000f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
100146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
100246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Stores mapping from type Strings exposed via {@link DataUsageFeedback} to
100346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * type integers in {@link DataUsageStatColumns}.
100446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
100546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final Map<String, Integer> sDataUsageTypeMap;
100646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
10074f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
10084f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
1009a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
1010d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
1011d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
1012a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_ID_DATA);
1013a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/entities", CONTACTS_ID_ENTITIES);
10143653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
10153653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
10162d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*",
10172d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                AGGREGATION_SUGGESTIONS);
1018a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_ID_PHOTO);
1019f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/display_photo",
1020f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_ID_DISPLAY_PHOTO);
10213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/stream_items",
10223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_ID_STREAM_ITEMS);
1023c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter", CONTACTS_FILTER);
10245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER);
10255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP);
10262149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/data", CONTACTS_LOOKUP_DATA);
10275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
10282149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/data",
10292149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                CONTACTS_LOOKUP_ID_DATA);
1030f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/display_photo",
1031f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_LOOKUP_DISPLAY_PHOTO);
1032f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/display_photo",
1033f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_LOOKUP_ID_DISPLAY_PHOTO);
1034a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/entities",
1035a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ENTITIES);
1036a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/entities",
1037a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ID_ENTITIES);
10383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/stream_items",
10393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_LOOKUP_STREAM_ITEMS);
10403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/stream_items",
10413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_LOOKUP_ID_STREAM_ITEMS);
1042f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_vcard/*", CONTACTS_AS_VCARD);
104342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_multi_vcard/*",
104442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                CONTACTS_AS_MULTI_VCARD);
10455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT);
1046ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*",
1047ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                CONTACTS_STREQUENT_FILTER);
10485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP);
104945ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa        matcher.addURI(ContactsContract.AUTHORITY, "contacts/frequent", CONTACTS_FREQUENT);
10503653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
10515ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
10525ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
10535ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA);
1054f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/display_photo",
1055f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                RAW_CONTACTS_ID_DISPLAY_PHOTO);
105646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ENTITY_ID);
10573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/stream_items",
10583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                RAW_CONTACTS_ID_STREAM_ITEMS);
105946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
106046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contact_entities", RAW_CONTACT_ENTITIES);
1061b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
10624f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
10634f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
1064ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
106548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID);
10665e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER);
1067ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
10684a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS);
106948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID);
10701dac83b8fa58944acfd00f44e717a7dddc659d2dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup", EMAILS_LOOKUP);
10715e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP);
10725e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER);
10734a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
1074ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
107548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
107646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        /** "*" is in CSV form with data ids ("123,456,789") */
107746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        matcher.addURI(ContactsContract.AUTHORITY, "data/usagefeedback/*", DATA_USAGE_FEEDBACK_ID);
10781f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1079ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
1080ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
1081ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
1082ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
108335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
1084b5a4add17815167d20a90645779df34cdf45280dFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#",
1085b5a4add17815167d20a90645779df34cdf45280dFred Quintana                SYNCSTATE_ID);
108635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1087a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
1088b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
1089b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
1090b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
1091b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
10924f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1093eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS);
1094eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
109582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates", STATUS_UPDATES);
109682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates/#", STATUS_UPDATES_ID);
10971f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1098c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
1099c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
1100c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
1101c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
11022d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*",
1103c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SHORTCUT);
1104c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
11051b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts",
11061b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS);
11071b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts/*",
11081b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_GROUP_NAME);
11091b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts_with_phones",
11101b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_WITH_PHONES);
11111b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/favorites",
11121b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_FAVORITES);
111309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
111409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "provider_status", PROVIDER_STATUS);
1115d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
1116d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories", DIRECTORIES);
1117d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories/#", DIRECTORIES_ID);
11187a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
11197a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "complete_name", COMPLETE_NAME);
112024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
112124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile", PROFILE);
112224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/entities", PROFILE_ENTITIES);
112324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/data", PROFILE_DATA);
112424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/data/#", PROFILE_DATA_ID);
112524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/as_vcard", PROFILE_AS_VCARD);
112624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts", PROFILE_RAW_CONTACTS);
112724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#",
112824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID);
112924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/data",
113024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID_DATA);
113124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/entity",
113224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID_ENTITIES);
113346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
11343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items", STREAM_ITEMS);
11353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/photo", STREAM_ITEMS_PHOTOS);
11363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#", STREAM_ITEMS_ID);
11373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#/photo", STREAM_ITEMS_ID_PHOTOS);
11383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#/photo/#",
11393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                STREAM_ITEMS_ID_PHOTOS_ID);
11403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items_limit", STREAM_ITEMS_LIMIT);
11413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
1142f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "display_photo/*", DISPLAY_PHOTO);
1143f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "photo_dimensions", PHOTO_DIMENSIONS);
1144f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
114546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        HashMap<String, Integer> tmpTypeMap = new HashMap<String, Integer>();
114646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_CALL, DataUsageStatColumns.USAGE_TYPE_INT_CALL);
114746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_LONG_TEXT,
114846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT);
114946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_SHORT_TEXT,
115046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DataUsageStatColumns.USAGE_TYPE_INT_SHORT_TEXT);
115146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        sDataUsageTypeMap = Collections.unmodifiableMap(tmpTypeMap);
115219a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    }
115319a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
1154d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static class DirectoryInfo {
1155d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String authority;
1156d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountName;
1157d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountType;
1158d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
1159d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
1160d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
1161d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Cached information about contact directories.
1162d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
11634458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private HashMap<String, DirectoryInfo> mDirectoryCache = new HashMap<String, DirectoryInfo>();
11644458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private boolean mDirectoryCacheValid = false;
1165d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
11663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
1167ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * An entry in group id cache. It maps the combination of (account type, account name
1168ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * and source id) to group row id.
1169ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     */
1170e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    public static class GroupIdCacheEntry {
1171ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType;
1172ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName;
1173ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String sourceId;
1174ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        long groupId;
1175ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    }
1176a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1177e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // We don't need a soft cache for groups - the assumption is that there will only
1178e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // be a small number of contact groups. The cache is keyed off source id.  The value
1179e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // is a list of groups with this group id.
1180e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    private HashMap<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache = Maps.newHashMap();
1181e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov
118224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
118324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Cached information about the contact ID and raw contact IDs that make up the user's
118424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * profile entry.
118524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
118624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static class ProfileIdCache {
118724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        boolean inited;
118824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        long profileContactId;
118924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        Set<Long> profileRawContactIds = Sets.newHashSet();
119024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        Set<Long> profileDataIds = Sets.newHashSet();
119124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
119224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        /**
119324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         * Initializes the cache of profile contact and raw contact IDs.  Does nothing if
119424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         * the cache is already initialized (unless forceRefresh is set to true).
119524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         * @param db The contacts database.
119624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         * @param forceRefresh Whether to force re-initialization of the cache.
119724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         */
119824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        private void init(SQLiteDatabase db, boolean forceRefresh) {
119924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            if (!inited || forceRefresh) {
120024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                profileContactId = 0;
120124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                profileRawContactIds.clear();
120224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                profileDataIds.clear();
120324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                Cursor c = db.rawQuery("SELECT " +
120424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        RawContactsColumns.CONCRETE_CONTACT_ID + "," +
120524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        RawContactsColumns.CONCRETE_ID + "," +
120624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        DataColumns.CONCRETE_ID +
120724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        " FROM " + Tables.RAW_CONTACTS + " JOIN " + Tables.ACCOUNTS + " ON " +
120824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        RawContactsColumns.CONCRETE_ID + "=" +
120924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        AccountsColumns.PROFILE_RAW_CONTACT_ID +
121024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        " JOIN " + Tables.DATA + " ON " +
121124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        RawContactsColumns.CONCRETE_ID + "=" + DataColumns.CONCRETE_RAW_CONTACT_ID,
121224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        null);
121324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                try {
121424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    while (c.moveToNext()) {
121524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        if (profileContactId == 0) {
121624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                            profileContactId = c.getLong(0);
121724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        }
121824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        profileRawContactIds.add(c.getLong(1));
121924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        profileDataIds.add(c.getLong(2));
122024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    }
122124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                } finally {
122224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    c.close();
122324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                }
122424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
122524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
122624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
122724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
122824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private ProfileIdCache mProfileIdCache;
122924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1230f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
1231f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Maximum dimension (height or width) of display photos.  Larger images will be scaled
1232f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * to fit.
1233f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
1234f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private int mMaxDisplayPhotoDim;
1235f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1236f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
1237f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Maximum dimension (height or width) of photo thumbnails.
1238f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
1239f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private int mMaxThumbnailPhotoDim;
1240f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
12413cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private HashMap<String, DataRowHandler> mDataRowHandlers;
1242b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private ContactsDatabaseHelper mDbHelper;
124331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
1244f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private PhotoStore mPhotoStore;
1245f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
12464097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
1247f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private NameLookupBuilder mNameLookupBuilder;
1248315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
1249622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private PostalSplitter mPostalSplitter;
1250622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
125172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    private ContactDirectoryManager mContactDirectoryManager;
1252622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private ContactAggregator mContactAggregator;
1253f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
1254a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    private GlobalSearchSupport mGlobalSearchSupport;
1255d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov    private CommonNicknameCache mCommonNicknameCache;
1256f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    private SearchIndexManager mSearchIndexManager;
1257a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
125820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private ContentValues mValues = new ContentValues();
125973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    private HashMap<String, Boolean> mAccountWritability = Maps.newHashMap();
126020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
126109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private int mProviderStatus = ProviderStatus.STATUS_NORMAL;
12623826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private boolean mProviderStatusUpdateNeeded;
126309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private long mEstimatedStorageRequirement = 0;
126415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private volatile CountDownLatch mReadAccessLatch;
126515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private volatile CountDownLatch mWriteAccessLatch;
126615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private boolean mAccountUpdateListenerRegistered;
1267bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private boolean mOkToOpenAccess = true;
126873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
1269d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov    private TransactionContext mTransactionContext = new TransactionContext();
1270de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov
12711a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey    private boolean mVisibleTouched = false;
12721a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
127381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    private boolean mSyncToNetwork;
127481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
12754cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    private Locale mCurrentLocale;
12763826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private int mContactsAccountCount;
1277d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov
1278bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private HandlerThread mBackgroundThread;
1279bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private Handler mBackgroundHandler;
1280bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1281f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private long mLastPhotoCleanup = 0;
1282f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
12834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
12844f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
1285de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        super.onCreate();
1286ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        try {
1287ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return initialize();
1288ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        } catch (RuntimeException e) {
1289ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            Log.e(TAG, "Cannot start provider", e);
1290ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return false;
1291ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        }
1292ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    }
129335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1294ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    private boolean initialize() {
129515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        StrictMode.setThreadPolicy(
129615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
129715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
12983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Resources resources = getContext().getResources();
1299f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mMaxDisplayPhotoDim = resources.getInteger(
1300f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                R.integer.config_max_display_photo_dim);
1301f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mMaxThumbnailPhotoDim = resources.getInteger(
1302f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                R.integer.config_max_thumbnail_photo_dim);
13033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
130424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        mProfileIdCache = new ProfileIdCache();
1305b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper = (ContactsDatabaseHelper)getDatabaseHelper();
130672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        mContactDirectoryManager = new ContactDirectoryManager(this);
1307a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mGlobalSearchSupport = new GlobalSearchSupport(this);
1308f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mPhotoStore = new PhotoStore(getContext().getFilesDir(), mDbHelper);
130965ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov
1310bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        // The provider is closed for business until fully initialized
131115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mReadAccessLatch = new CountDownLatch(1);
131215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mWriteAccessLatch = new CountDownLatch(1);
131372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
1314bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundThread = new HandlerThread("ContactsProviderWorker",
1315bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                Process.THREAD_PRIORITY_BACKGROUND);
1316bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundThread.start();
1317bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler = new Handler(mBackgroundThread.getLooper()) {
1318bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            @Override
1319bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            public void handleMessage(Message msg) {
1320bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                performBackgroundTask(msg.what, msg.obj);
1321bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1322bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        };
13232a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov
132415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_INITIALIZE);
1325bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS);
1326bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
1327bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_LOCALE);
1328bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM);
132905e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_SEARCH_INDEX);
1330bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_PROVIDER_STATUS);
133115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_OPEN_WRITE_ACCESS);
1332f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
13333826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
133449d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov        return true;
13354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
13364f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1337767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    /**
133851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * (Re)allocates all locale-sensitive structures.
133951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
134004b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov    private void initForDefaultLocale() {
134115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        Context context = getContext();
134215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mLegacyApiSupport = new LegacyApiSupport(context, mDbHelper, this, mGlobalSearchSupport);
13434cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mCurrentLocale = getLocale();
134404b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov        mNameSplitter = mDbHelper.createNameSplitter();
13454cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
13464cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mPostalSplitter = new PostalSplitter(mCurrentLocale);
134751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        mCommonNicknameCache = new CommonNicknameCache(mDbHelper.getReadableDatabase());
1348cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao        ContactLocaleUtils.getIntance().setLocale(mCurrentLocale);
13495b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator = new ContactAggregator(this, mDbHelper,
135015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                createPhotoPriorityResolver(context), mNameSplitter, mCommonNicknameCache);
13515b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
1352f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov        mSearchIndexManager = new SearchIndexManager(this);
13535b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
1354bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers = new HashMap<String, DataRowHandler>();
1355bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1356bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Email.CONTENT_ITEM_TYPE,
13576d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForEmail(context, mDbHelper, mContactAggregator));
1358bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Im.CONTENT_ITEM_TYPE,
13596d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForIm(context, mDbHelper, mContactAggregator));
1360bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Organization.CONTENT_ITEM_TYPE,
13616d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForOrganization(context, mDbHelper, mContactAggregator));
1362bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Phone.CONTENT_ITEM_TYPE,
13636d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForPhoneNumber(context, mDbHelper, mContactAggregator));
1364bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE,
13656d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForNickname(context, mDbHelper, mContactAggregator));
1366bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(StructuredName.CONTENT_ITEM_TYPE,
13676d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForStructuredName(context, mDbHelper, mContactAggregator,
1368bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mNameSplitter, mNameLookupBuilder));
1369bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(StructuredPostal.CONTENT_ITEM_TYPE,
13706d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForStructuredPostal(context, mDbHelper, mContactAggregator,
1371bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mPostalSplitter));
1372bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(GroupMembership.CONTENT_ITEM_TYPE,
13736d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForGroupMembership(context, mDbHelper, mContactAggregator,
1374bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mGroupIdCache));
1375bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Photo.CONTENT_ITEM_TYPE,
1376f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                new DataRowHandlerForPhoto(context, mDbHelper, mContactAggregator, mPhotoStore));
13776d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov        mDataRowHandlers.put(Note.CONTENT_ITEM_TYPE,
13786d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForNote(context, mDbHelper, mContactAggregator));
1379bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1380bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1381bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    /**
1382bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     * Visible for testing.
1383bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     */
1384bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    /* package */ PhotoPriorityResolver createPhotoPriorityResolver(Context context) {
1385bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        return new PhotoPriorityResolver(context);
1386bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1387bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1388bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task) {
1389bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendEmptyMessage(task);
1390bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1391bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1392bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task, Object arg) {
1393bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendMessage(mBackgroundHandler.obtainMessage(task, arg));
1394bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1395bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1396bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void performBackgroundTask(int task, Object arg) {
1397bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        switch (task) {
139815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_INITIALIZE: {
139915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                initForDefaultLocale();
140015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch.countDown();
140115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch = null;
140215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                break;
140315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            }
140415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
140515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_OPEN_WRITE_ACCESS: {
1406bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (mOkToOpenAccess) {
140715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch.countDown();
140815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch = null;
1409bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1410bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1411bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1412bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1413bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS: {
1414bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (isLegacyContactImportNeeded()) {
1415bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    importLegacyContactsInBackground();
1416bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1417bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1418bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1419bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1420bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_ACCOUNTS: {
142115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Context context = getContext();
142215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                if (!mAccountUpdateListenerRegistered) {
142315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    AccountManager.get(context).addOnAccountsUpdatedListener(this, null, false);
142415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mAccountUpdateListenerRegistered = true;
142515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                }
142615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
142715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Account[] accounts = AccountManager.get(context).getAccounts();
1428bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                boolean accountsChanged = updateAccountsInBackground(accounts);
1429bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateContactsAccountCount(accounts);
1430bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateDirectoriesInBackground(accountsChanged);
1431bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1432bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1433bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1434bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_LOCALE: {
1435bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateLocaleInBackground();
1436bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1437bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1438bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1439fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            case BACKGROUND_TASK_CHANGE_LOCALE: {
1440fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                changeLocaleInBackground();
1441fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                break;
1442fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            }
1443fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1444bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM: {
1445bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (isAggregationUpgradeNeeded()) {
1446bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    upgradeAggregationAlgorithmInBackground();
1447bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1448bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1449bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1450bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
145105e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_SEARCH_INDEX: {
145205e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                updateSearchIndexInBackground();
145305e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                break;
145405e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            }
145505e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
1456bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_PROVIDER_STATUS: {
1457bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateProviderStatus();
1458bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1459bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1460bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1461bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_DIRECTORIES: {
1462bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (arg != null) {
1463bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    mContactDirectoryManager.onPackageChanged((String) arg);
1464bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1465bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1466bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1467f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1468f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case BACKGROUND_TASK_CLEANUP_PHOTOS: {
1469f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Check rate limit.
1470f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long now = System.currentTimeMillis();
1471f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (now - mLastPhotoCleanup > PHOTO_CLEANUP_RATE_LIMIT) {
1472f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    mLastPhotoCleanup = now;
1473f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    cleanupPhotoStore();
1474f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    break;
1475f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
1476f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1477bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
14784cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
14794cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao
148053fac8f99f3884c372c907a76766d27fa9e1d95fDmitri Plotnikov    public void onLocaleChanged() {
14813826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
14823826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
14834f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov            return;
14844f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov        }
14854f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov
1486fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_CHANGE_LOCALE);
14874cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
148851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
148951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    /**
149051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * Verifies that the contacts database is properly configured for the current locale.
149151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * If not, changes the database locale to the current locale using an asynchronous task.
149251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * This needs to be done asynchronously because the process involves rebuilding
149351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * large data structures (name lookup, sort keys), which can take minutes on
149451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * a large set of contacts.
149551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
1496bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateLocaleInBackground() {
1497f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
1498f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        // The process is already running - postpone the change
1499f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        if (mProviderStatus == ProviderStatus.STATUS_CHANGING_LOCALE) {
1500f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov            return;
1501f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        }
1502f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
150351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
150451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final String providerLocale = prefs.getString(PREF_LOCALE, null);
150551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final Locale currentLocale = mCurrentLocale;
150651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        if (currentLocale.toString().equals(providerLocale)) {
150751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            return;
150851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        }
150951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
151051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        int providerStatus = mProviderStatus;
151151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_CHANGING_LOCALE);
1512bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDbHelper.setLocale(this, currentLocale);
1513bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        prefs.edit().putString(PREF_LOCALE, currentLocale.toString()).apply();
1514bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        setProviderStatus(providerStatus);
1515bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
151651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
1517fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    /**
1518fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     * Reinitializes the provider for a new locale.
1519fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     */
1520fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    private void changeLocaleInBackground() {
1521fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Re-initializing the provider without stopping it.
1522fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Locking the database will prevent inserts/updates/deletes from
1523fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // running at the same time, but queries may still be running
1524fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // on other threads. Those queries may return inconsistent results.
1525fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        SQLiteDatabase db = mDbHelper.getWritableDatabase();
1526fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        db.beginTransaction();
1527fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        try {
1528fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            initForDefaultLocale();
1529fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.setTransactionSuccessful();
1530fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        } finally {
1531fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.endTransaction();
1532fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        }
1533fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1534fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        updateLocaleInBackground();
1535fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    }
1536fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
153705e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    protected void updateSearchIndexInBackground() {
153805e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        mSearchIndexManager.updateIndex();
153905e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    }
154005e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
1541bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateDirectoriesInBackground(boolean rescan) {
1542bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mContactDirectoryManager.scanAllPackages(rescan);
154351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
154451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
15453826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateProviderStatus() {
15463826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
15473826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
15483826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return;
15493826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
15503826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
15513826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mContactsAccountCount == 0
155249d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                && DatabaseUtils.queryNumEntries(mDbHelper.getReadableDatabase(),
155349d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                        Tables.CONTACTS, null) == 0) {
15543826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            setProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS);
15553826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } else {
15563826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            setProviderStatus(ProviderStatus.STATUS_NORMAL);
15573826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
15583826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
15593826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
156031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
1561f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    protected void cleanupPhotoStore() {
15626802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        SQLiteDatabase db = mDbHelper.getWritableDatabase();
15636802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
15646802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Assemble the set of photo store file IDs that are in use, and send those to the photo
1565f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // store.  Any photos that aren't in that set will be deleted, and any photos that no
1566f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // longer exist in the photo store will be returned for us to clear out in the DB.
15676802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Cursor c = db.query(Views.DATA, new String[]{Data._ID, Photo.PHOTO_FILE_ID},
1568f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                Data.MIMETYPE + "=" + Photo.MIMETYPE + " AND "
1569f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        + Photo.PHOTO_FILE_ID + " IS NOT NULL", null, null, null, null);
15706802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Set<Long> usedPhotoFileIds = Sets.newHashSet();
15716802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> photoFileIdToDataId = Maps.newHashMap();
1572f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        try {
1573f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            while (c.moveToNext()) {
15746802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long dataId = c.getLong(0);
15756802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long photoFileId = c.getLong(1);
15766802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                usedPhotoFileIds.add(photoFileId);
15776802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                photoFileIdToDataId.put(photoFileId, dataId);
15786802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
15796802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        } finally {
15806802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            c.close();
15816802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
15826802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
15836802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Also query for all social stream item photos.
15846802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        c = db.query(Tables.STREAM_ITEM_PHOTOS,
15856802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                new String[]{
15866802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        StreamItemPhotos._ID,
15876802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        StreamItemPhotos.STREAM_ITEM_ID,
15886802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        StreamItemPhotos.PHOTO_FILE_ID
15896802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                },
15906802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                null, null, null, null, null);
15916802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> photoFileIdToStreamItemPhotoId = Maps.newHashMap();
15926802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> streamItemPhotoIdToStreamItemId = Maps.newHashMap();
15936802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        try {
15946802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            while (c.moveToNext()) {
15956802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long streamItemPhotoId = c.getLong(0);
15966802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long streamItemId = c.getLong(1);
15976802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long photoFileId = c.getLong(2);
15986802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                usedPhotoFileIds.add(photoFileId);
15996802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                photoFileIdToStreamItemPhotoId.put(photoFileId, streamItemPhotoId);
16006802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                streamItemPhotoIdToStreamItemId.put(streamItemPhotoId, streamItemId);
1601f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1602f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } finally {
1603f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            c.close();
1604f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
1605f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1606f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // Run the photo store cleanup.
16076802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Set<Long> missingPhotoIds = mPhotoStore.cleanup(usedPhotoFileIds);
1608f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1609f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // If any of the keys we're using no longer exist, clean them up.
16106802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (!missingPhotoIds.isEmpty()) {
1611f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            ArrayList<ContentProviderOperation> ops = Lists.newArrayList();
16126802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            for (long missingPhotoId : missingPhotoIds) {
16136802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (photoFileIdToDataId.containsKey(missingPhotoId)) {
16146802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    long dataId = photoFileIdToDataId.get(missingPhotoId);
1615f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    ContentValues updateValues = new ContentValues();
1616f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    updateValues.putNull(Photo.PHOTO_FILE_ID);
1617f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    ops.add(ContentProviderOperation.newUpdate(
16186802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            ContentUris.withAppendedId(Data.CONTENT_URI, dataId))
1619f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            .withValues(updateValues).build());
1620f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
16216802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (photoFileIdToStreamItemPhotoId.containsKey(missingPhotoId)) {
16226802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // For missing photos that were in stream item photos, just delete the stream
16236802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // item photo.
16246802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    long streamItemPhotoId = photoFileIdToStreamItemPhotoId.get(missingPhotoId);
16256802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    long streamItemId = streamItemPhotoIdToStreamItemId.get(streamItemPhotoId);
16266802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    ops.add(ContentProviderOperation.newDelete(
16276802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            StreamItems.CONTENT_URI.buildUpon()
16286802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    .appendPath(String.valueOf(streamItemId))
16296802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    .appendPath(StreamItems.StreamItemPhotos.CONTENT_DIRECTORY)
16306802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    .appendPath(String.valueOf(streamItemPhotoId))
16316802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    .build()).build());
16326802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                }
1633f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1634f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            try {
1635f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                applyBatch(ops);
1636f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            } catch (OperationApplicationException oae) {
1637f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Not a fatal problem (and we'll try again on the next cleanup).
1638f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                Log.e(TAG, "Failed to clean up outdated photo references", oae);
1639f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1640f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
1641f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
1642f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1643f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /* Visible for testing */
1644de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    @Override
1645b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
1646b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        return ContactsDatabaseHelper.getInstance(context);
164731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
164831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
1649f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /* package */ PhotoStore getPhotoStore() {
1650f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        return mPhotoStore;
1651f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
1652f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
165387614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    /* package */ int getMaxDisplayPhotoDim() {
165487614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro        return mMaxDisplayPhotoDim;
165587614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    }
165687614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro
165787614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    /* package */ int getMaxThumbnailPhotoDim() {
165887614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro        return mMaxThumbnailPhotoDim;
165987614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    }
166087614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro
1661013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    /* package */ NameSplitter getNameSplitter() {
1662013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        return mNameSplitter;
1663013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    }
1664013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov
16655df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    /* package */ NameLookupBuilder getNameLookupBuilder() {
16665df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov        return mNameLookupBuilder;
16675df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    }
16685df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov
16695dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    /* Visible for testing */
1670ed78fd6df5e9f3a2d572162e5d374d1f4a625bddDmitri Plotnikov    public ContactDirectoryManager getContactDirectoryManagerForTest() {
167172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        return mContactDirectoryManager;
167272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
167372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
167472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    /* Visible for testing */
16755dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    protected Locale getLocale() {
16765dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        return Locale.getDefault();
16775dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
16785dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
16793d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    protected boolean isLegacyContactImportNeeded() {
1680b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        int version = Integer.parseInt(mDbHelper.getProperty(PROPERTY_CONTACTS_IMPORTED, "0"));
1681b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        return version < PROPERTY_CONTACTS_IMPORT_VERSION;
16823d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
16833d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1684568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected LegacyContactImporter getLegacyContactImporter() {
1685568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return new LegacyContactImporter(getContext(), this);
1686568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1687568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1688568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
1689bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     * Imports legacy contacts as a background task.
1690568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
1691bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private void importLegacyContactsInBackground() {
1692bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Importing legacy contacts");
1693bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADING);
1694568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1695bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
1696bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDbHelper.setLocale(this, mCurrentLocale);
1697bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        prefs.edit().putString(PREF_LOCALE, mCurrentLocale.toString()).commit();
1698568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1699bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        LegacyContactImporter importer = getLegacyContactImporter();
1700bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        if (importLegacyContacts(importer)) {
1701bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            onLegacyContactImportSuccess();
1702bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        } else {
1703bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            onLegacyContactImportFailure();
1704bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
1705568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1706568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1707bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1708bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Unlocks the provider and declares that the import process is complete.
1709bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1710bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportSuccess() {
1711bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
1712bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)getContext().getSystemService(Context.NOTIFICATION_SERVICE);
1713bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.cancel(LEGACY_IMPORT_FAILED_NOTIFICATION);
1714bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1715b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        // Store a property in the database indicating that the conversion process succeeded
1716b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mDbHelper.setProperty(PROPERTY_CONTACTS_IMPORTED,
1717b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov                String.valueOf(PROPERTY_CONTACTS_IMPORT_VERSION));
1718bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_NORMAL);
1719bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Completed import of legacy contacts");
1720bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    }
1721bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1722bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1723bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Announces the provider status and keeps the provider locked.
1724bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1725bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportFailure() {
1726bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Context context = getContext();
1727bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
1728bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
1729bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1730bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        // Show a notification
1731bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Notification n = new Notification(android.R.drawable.stat_notify_error,
1732bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_ticker),
1733bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                System.currentTimeMillis());
1734bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.setLatestEventInfo(context,
1735bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_title),
1736bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_text),
1737bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                PendingIntent.getActivity(context, 0, new Intent(Intents.UI.LIST_DEFAULT), 0));
1738bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
1739bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1740bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.notify(LEGACY_IMPORT_FAILED_NOTIFICATION, n);
1741bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1742bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADE_OUT_OF_MEMORY);
1743bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Failed to import legacy contacts");
1744bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1745bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        // Do not let any database changes until this issue is resolved.
1746bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mOkToOpenAccess = false;
17473d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
17483d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
17493d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /* Visible for testing */
1750568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
17510e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        boolean aggregatorEnabled = mContactAggregator.isEnabled();
17523d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mContactAggregator.setEnabled(false);
17533d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        try {
1754bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            if (importer.importContacts()) {
1755bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1756bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                // TODO aggregate all newly added raw contacts
1757bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                mContactAggregator.setEnabled(aggregatorEnabled);
1758bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                return true;
1759bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
17603d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } catch (Throwable e) {
17613d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           Log.e(TAG, "Legacy contact import failed", e);
17623d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
1763bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mEstimatedStorageRequirement = importer.getEstimatedStorageRequirement();
1764bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        return false;
17653d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
17663d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1767a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
1768a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
1769a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
1770a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
1771b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper.wipeData();
1772f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mPhotoStore.clear();
17733826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatus = ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS;
1774a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1775a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1776568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
177715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov     * During intialization, this content provider will
1778568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * block all attempts to change contacts data. In particular, it will hold
1779568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * up all contact syncs. As soon as the import process is complete, all
1780568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * processes waiting to write to the provider are unblocked and can proceed
1781568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * to compete for the database transaction monitor.
1782568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
178315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private void waitForAccess(CountDownLatch latch) {
178415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (latch == null) {
178515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            return;
178615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        }
178715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
178815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        while (true) {
178915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            try {
179015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                latch.await();
179115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                return;
179215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            } catch (InterruptedException e) {
179315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Thread.currentThread().interrupt();
1794ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            }
1795568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
1796568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1797568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1798568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1799568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
180015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1801568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.insert(uri, values);
1802568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1803568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1804568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1805568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
180615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (mWriteAccessLatch != null) {
1807bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // We are stuck trying to upgrade contacts db.  The only update request
1808bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // allowed in this case is an update of provider status, which will trigger
1809bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // an attempt to upgrade contacts again.
1810bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            int match = sUriMatcher.match(uri);
1811bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            if (match == PROVIDER_STATUS) {
1812bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                Integer newStatus = values.getAsInteger(ProviderStatus.STATUS);
1813bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                if (newStatus != null && newStatus == ProviderStatus.STATUS_UPGRADING) {
1814bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    scheduleBackgroundTask(BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS);
1815bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 1;
1816bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                } else {
1817bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 0;
1818bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                }
1819bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
1820bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
182115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1822568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.update(uri, values, selection, selectionArgs);
1823568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1824568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1825568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1826568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
182715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1828568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.delete(uri, selection, selectionArgs);
1829568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1830568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1831568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1832568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
1833568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            throws OperationApplicationException {
183415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1835568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.applyBatch(operations);
1836568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1837568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
18384f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
18397b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    public int bulkInsert(Uri uri, ContentValues[] values) {
18407b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov        waitForAccess(mWriteAccessLatch);
18417b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov        return super.bulkInsert(uri, values);
18427b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    }
18437b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov
18447b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    @Override
1845285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void onBeginTransaction() {
1846bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1847b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "onBeginTransaction");
1848b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1849285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.onBeginTransaction();
18501ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.clearPendingAggregations();
1851d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.clear();
1852b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1853b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1854285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1855285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
1856285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void beforeTransactionCommit() {
18571129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
1858bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1859b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "beforeTransactionCommit");
1860b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1861285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.beforeTransactionCommit();
1862b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
1863bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        mContactAggregator.aggregateInTransaction(mTransactionContext, mDb);
18641a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (mVisibleTouched) {
18651a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = false;
1866b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.updateAllVisible();
18671a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        }
18683826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
1869bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        updateSearchIndexInTransaction();
1870bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov
18713826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatusUpdateNeeded) {
18723826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
18733826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatusUpdateNeeded = false;
18743826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
1875b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1876b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1877bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov    private void updateSearchIndexInTransaction() {
1878bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        Set<Long> staleContacts = mTransactionContext.getStaleSearchIndexContactIds();
1879bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        Set<Long> staleRawContacts = mTransactionContext.getStaleSearchIndexRawContactIds();
1880bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        if (!staleContacts.isEmpty() || !staleRawContacts.isEmpty()) {
1881bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mSearchIndexManager.updateIndexForRawContacts(staleContacts, staleRawContacts);
1882bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mTransactionContext.clearSearchIndexUpdates();
1883bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        }
1884bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov    }
1885bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov
1886b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void flushTransactionalChanges() {
1887bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1888b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "flushTransactionChanges");
1889b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
18901129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
189124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // Determine whether we need to refresh the profile ID cache.
189224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        boolean profileCacheRefreshNeeded = false;
189324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1894d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        for (long rawContactId : mTransactionContext.getInsertedRawContactIds()) {
18958ab0b7a48efe540226253567bcf6fdbc487186a2Dmitri Plotnikov            mDbHelper.updateRawContactDisplayName(mDb, rawContactId);
1896bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mContactAggregator.onRawContactInsert(mTransactionContext, mDb, rawContactId);
1897285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        }
1898b5a4add17815167d20a90645779df34cdf45280dFred Quintana
189924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        Map<Long, Account> insertedProfileRawContactAccountMap =
190024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                mTransactionContext.getInsertedProfileRawContactIds();
190124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (!insertedProfileRawContactAccountMap.isEmpty()) {
190224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            for (long profileRawContactId : insertedProfileRawContactAccountMap.keySet()) {
190324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                mDbHelper.updateRawContactDisplayName(mDb, profileRawContactId);
190424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                mContactAggregator.onProfileRawContactInsert(mTransactionContext, mDb,
190524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        profileRawContactId,
190624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        insertedProfileRawContactAccountMap.get(profileRawContactId));
190724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
190824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            profileCacheRefreshNeeded = true;
190924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
191024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1911d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        Set<Long> dirtyRawContacts = mTransactionContext.getDirtyRawContactIds();
1912d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!dirtyRawContacts.isEmpty()) {
1913a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
1914a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_DIRTY_SQL);
1915d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, dirtyRawContacts);
1916a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
1917a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
191824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
191924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            profileCacheRefreshNeeded = profileCacheRefreshNeeded ||
192024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    !Collections.disjoint(mProfileIdCache.profileRawContactIds, dirtyRawContacts);
1921a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        }
1922a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
1923d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        Set<Long> updatedRawContacts = mTransactionContext.getUpdatedRawContactIds();
1924d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!updatedRawContacts.isEmpty()) {
1925a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
1926a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_VERSION_SQL);
1927d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, updatedRawContacts);
1928a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
1929a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
193024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
193124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            profileCacheRefreshNeeded = profileCacheRefreshNeeded ||
193224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    !Collections.disjoint(mProfileIdCache.profileRawContactIds, updatedRawContacts);
1933b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1934b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1935d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        for (Map.Entry<Long, Object> entry : mTransactionContext.getUpdatedSyncStates()) {
1936b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long id = entry.getKey();
19379d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            if (mDbHelper.getSyncState().update(mDb, id, entry.getValue()) <= 0) {
19389d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                throw new IllegalStateException(
19399d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                        "unable to update sync state, does it still exist?");
19409d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            }
1941b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1942b5a4add17815167d20a90645779df34cdf45280dFred Quintana
194324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (profileCacheRefreshNeeded) {
194424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Force the profile ID cache to refresh.
194524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            mProfileIdCache.init(mDb, true);
194624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
194724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1948d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.clear();
1949b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1950b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1951a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /**
1952a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * Appends comma separated ids.
1953a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * @param ids Should not be empty
1954a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     */
1955d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov    private void appendIds(StringBuilder sb, Set<Long> ids) {
1956b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long id : ids) {
1957a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            sb.append(id).append(',');
1958b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1959a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
1960a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        sb.setLength(sb.length() - 1); // Yank the last comma
1961285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
1962285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
196324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
196424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Checks whether the given contact ID represents the user's personal profile - if it is, calls
196524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * a permission check (for writing the profile if forWrite is true, for reading the profile
196624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * otherwise).  If the contact ID is not the user's profile, no check is executed.
1967afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro     * @param db The database.
196824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param contactId The contact ID to be checked.
196924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forWrite Whether the caller is attempting to do a write (vs. read) operation.
197024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
1971afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro    private void enforceProfilePermissionForContact(SQLiteDatabase db, long contactId,
1972afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro            boolean forWrite) {
1973afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        mProfileIdCache.init(db, false);
197424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (mProfileIdCache.profileContactId == contactId) {
197524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermission(forWrite);
197624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
197724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
197824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
197924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
198024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Checks whether the given raw contact ID is a member of the user's personal profile - if it
198124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * is, calls a permission check (for writing the profile if forWrite is true, for reading the
198224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * profile otherwise).  If the raw contact ID is not in the user's profile, no check is
198324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * executed.
1984afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro     * @param db The database.
198524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param rawContactId The raw contact ID to be checked.
198624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forWrite Whether the caller is attempting to do a write (vs. read) operation.
198724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
1988afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro    private void enforceProfilePermissionForRawContact(SQLiteDatabase db, long rawContactId,
1989afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro            boolean forWrite) {
1990afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        mProfileIdCache.init(db, false);
199124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (mProfileIdCache.profileRawContactIds.contains(rawContactId)) {
199224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermission(forWrite);
199324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
199424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
199524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
199624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
199724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Checks whether the given data ID is a member of the user's personal profile - if it is,
199824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * calls a permission check (for writing the profile if forWrite is true, for reading the
199924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * profile otherwise).  If the data ID is not in the user's profile, no check is executed.
2000afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro     * @param db The database.
200124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param dataId The data ID to be checked.
200224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forWrite Whether the caller is attempting to do a write (vs. read) operation.
200324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
2004afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro    private void enforceProfilePermissionForData(SQLiteDatabase db, long dataId, boolean forWrite) {
2005afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        mProfileIdCache.init(db, false);
200624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (mProfileIdCache.profileDataIds.contains(dataId)) {
200724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermission(forWrite);
200824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
200924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
201024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
201124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
201224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Performs a permission check for WRITE_PROFILE or READ_PROFILE (depending on the parameter).
201324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * If the permission check fails, this will throw a SecurityException.
201424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forWrite Whether the caller is attempting to do a write (vs. read) operation.
201524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
201624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private void enforceProfilePermission(boolean forWrite) {
201724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        String profilePermission = forWrite
201824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                ? "android.permission.WRITE_PROFILE"
201924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                : "android.permission.READ_PROFILE";
202024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        getContext().enforceCallingOrSelfPermission(profilePermission, null);
202124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
202224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2023285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2024cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    protected void notifyChange() {
202581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        notifyChange(mSyncToNetwork);
202681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = false;
202781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
202881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
202981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    protected void notifyChange(boolean syncToNetwork) {
203081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
203181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                syncToNetwork);
2032cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    }
2033568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
203451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    protected void setProviderStatus(int status) {
20353826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != status) {
20363826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatus = status;
20373826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            getContext().getContentResolver().notifyChange(ProviderStatus.CONTENT_URI, null, false);
20383826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
203951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
204051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
2041f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    public DataRowHandler getDataRowHandler(final String mimeType) {
20423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
20433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
20446d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov            handler = new DataRowHandlerForCustomMimetype(
20456d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                    getContext(), mDbHelper, mContactAggregator, mimeType);
20463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
20473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
20483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
20493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
20503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
20514f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2052de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected Uri insertInTransaction(Uri uri, ContentValues values) {
2053bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
20541129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Log.v(TAG, "insertInTransaction: " + uri + " " + values);
2055b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2056f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2057f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2058f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2059f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2060a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
2061a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
206235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2063a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
206435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2065b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                id = mDbHelper.getSyncState().insert(mDb, values);
206635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
206735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2068d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
2069d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                insertContact(values);
20706bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
20716bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
20726bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
207324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
207424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                throw new UnsupportedOperationException(
207524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        "The profile contact is created automatically");
207624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
207724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
20785ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
207924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                id = insertRawContact(uri, values, callerIsSyncAdapter, false);
2080f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2081a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2082a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2083a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
20845ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
20855ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(1));
2086f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2087f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2088a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2089a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2090a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
20913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case RAW_CONTACTS_ID_STREAM_ITEMS: {
20923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                values.put(StreamItems.RAW_CONTACT_ID, uri.getPathSegments().get(1));
20933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItem(uri, values);
20943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
20953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
20963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
20973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
209824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS: {
209924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(true);
210024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                id = insertRawContact(uri, values, callerIsSyncAdapter, true);
210124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                mSyncToNetwork |= !callerIsSyncAdapter;
210224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
210324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
210424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2105a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case DATA: {
2106f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2107f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2108a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2109a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2110a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2111ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
2112f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                id = insertGroup(uri, values, callerIsSyncAdapter);
2113f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2114ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2115ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2116ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2117eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
21185aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertSettings(uri, values);
211943880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2120eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
2121eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2122eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
212382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
212482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                id = insertStatusUpdate(values);
21251f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
21261f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
21271f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
21283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
21293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItem(uri, values);
21303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
21313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
21323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
21333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
21343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
21353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItemPhoto(uri, values);
21363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
21373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
21383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
21393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
21403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
21413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                values.put(StreamItemPhotos.STREAM_ITEM_ID, uri.getPathSegments().get(1));
21423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItemPhoto(uri, values);
21433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
21443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
21453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
21463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
2147a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
214881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
2149f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
2150a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2151a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
21527e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
21537e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
21547e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
21557e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2156de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        return ContentUris.withAppendedId(uri, id);
2157a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2158a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2159a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2160e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * If account is non-null then store it in the values. If the account is
2161e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * already specified in the values then it must be consistent with the
2162e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * account, if it is non-null.
2163e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *
2164e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param uri Current {@link Uri} being operated on.
2165e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param values {@link ContentValues} to read and possibly update.
2166e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when only one of
2167e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_NAME} or
2168e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_TYPE} is specified, leaving the
2169e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             other undefined.
2170e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when {@link RawContacts#ACCOUNT_NAME}
2171e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             and {@link RawContacts#ACCOUNT_TYPE} are inconsistent between
2172e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             the given {@link Uri} and {@link ContentValues}.
21737e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
2174e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey    private Account resolveAccount(Uri uri, ContentValues values) throws IllegalArgumentException {
2175f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
2176f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
2177e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
2178f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2179f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
2180f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
2181e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialValues = TextUtils.isEmpty(valueAccountName)
2182e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                ^ TextUtils.isEmpty(valueAccountType);
2183e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2184e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri || partialValues) {
2185e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
2186fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
2187fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
2188e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
2189e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2190e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
2191e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
2192e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validUri = !TextUtils.isEmpty(accountName);
2193e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validValues = !TextUtils.isEmpty(valueAccountName);
2194e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2195e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validValues && validUri) {
2196e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Check that accounts match when both present
2197e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            final boolean accountMatch = TextUtils.equals(accountName, valueAccountName)
2198e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    && TextUtils.equals(accountType, valueAccountType);
2199e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            if (!accountMatch) {
2200fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new IllegalArgumentException(mDbHelper.exceptionMessage(
2201fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        "When both specified, ACCOUNT_NAME and ACCOUNT_TYPE must match", uri));
2202e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            }
2203e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validUri) {
2204e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Fill values from Uri when not present
2205f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_NAME, accountName);
2206f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_TYPE, accountType);
2207e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validValues) {
2208f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountName = valueAccountName;
2209f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountType = valueAccountType;
2210e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else {
2211e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            return null;
2212f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
2213f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2214e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Use cached Account object when matches, otherwise create
2215f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mAccount == null
2216f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.name.equals(accountName)
2217f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.type.equals(accountType)) {
2218f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mAccount = new Account(accountName, accountType);
2219035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
2220f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2221e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        return mAccount;
22227e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
22237e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
22247e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
2225d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Inserts an item in the contacts table
22266bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
22276bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
22286bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
22296bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
2230d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertContact(ContentValues values) {
2231de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregate contacts are created automatically");
22326bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
22336bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
22346bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
223524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Inserts an item in the raw contacts table
2236a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2237f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param uri the values for the new row
2238f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param values the account this contact should be associated with. may be null.
2239dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana     * @param callerIsSyncAdapter
224024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forProfile Whether this raw contact is being inserted into the user's profile.
2241a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2242a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
224324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private long insertRawContact(Uri uri, ContentValues values, boolean callerIsSyncAdapter,
224424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            boolean forProfile) {
2245f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2246f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2247f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2248f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2249e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final Account account = resolveAccount(uri, mValues);
22507e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
22513d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
22523d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
2253f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
22543d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
22553d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2256f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long rawContactId = mDb.insert(Tables.RAW_CONTACTS, RawContacts.CONTACT_ID, mValues);
2257f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        int aggregationMode = RawContacts.AGGREGATION_MODE_DEFAULT;
225824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (forProfile) {
225924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Profile raw contacts should never be aggregated by the aggregator; they are always
226024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // aggregated under a single profile contact.
226124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            aggregationMode = RawContacts.AGGREGATION_MODE_DISABLED;
226224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        } else if (mValues.containsKey(RawContacts.AGGREGATION_MODE)) {
2263f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            aggregationMode = mValues.getAsInteger(RawContacts.AGGREGATION_MODE);
2264f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        }
2265f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        mContactAggregator.markNewForAggregation(rawContactId, aggregationMode);
2266285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
226724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (forProfile) {
226824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Trigger creation of the user profile Contact (or association with the existing one)
226924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // at the end of the transaction.
227024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            mTransactionContext.profileRawContactInserted(rawContactId, account);
227124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        } else {
227224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Trigger creation of a Contact based on this RawContact at the end of transaction
227324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            mTransactionContext.rawContactInserted(rawContactId, account);
227424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
2275f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2276dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter) {
2277dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            addAutoAddMembership(rawContactId);
2278dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            final Long starred = values.getAsLong(RawContacts.STARRED);
2279dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (starred != null && starred != 0) {
2280dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateFavoritesMembership(rawContactId, starred != 0);
2281dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2282dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2283dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
22843826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
2285023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        return rawContactId;
2286a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2287a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2288dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void addAutoAddMembership(long rawContactId) {
2289dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID,
2290dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2291dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2292dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            insertDataGroupMembership(rawContactId, groupId);
2293dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2294dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2295dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2296dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private Long findGroupByRawContactId(String selection, long rawContactId) {
2297dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        Cursor c = mDb.query(Tables.GROUPS + "," + Tables.RAW_CONTACTS, PROJECTION_GROUP_ID,
2298dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection,
2299dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                new String[]{Long.toString(rawContactId)},
2300dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                null /* groupBy */, null /* having */, null /* orderBy */);
2301dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        try {
2302dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            while (c.moveToNext()) {
2303dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return c.getLong(0);
2304dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2305dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return null;
2306dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        } finally {
2307dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            c.close();
2308dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2309dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2310dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2311dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void updateFavoritesMembership(long rawContactId, boolean isStarred) {
2312dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID,
2313dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2314dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2315dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (isStarred) {
2316dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                insertDataGroupMembership(rawContactId, groupId);
2317dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
2318dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                deleteDataGroupMembership(rawContactId, groupId);
2319dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2320dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2321dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2322dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2323dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void insertDataGroupMembership(long rawContactId, long groupId) {
2324dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        ContentValues groupMembershipValues = new ContentValues();
2325dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.GROUP_ROW_ID, groupId);
2326dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.RAW_CONTACT_ID, rawContactId);
2327dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(DataColumns.MIMETYPE_ID,
2328dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                mDbHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
2329dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mDb.insert(Tables.DATA, null, groupMembershipValues);
2330dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2331dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2332dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void deleteDataGroupMembership(long rawContactId, long groupId) {
2333dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final String[] selectionArgs = {
2334dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(mDbHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE)),
2335dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(groupId),
2336dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(rawContactId)};
2337dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mDb.delete(Tables.DATA, SELECTION_GROUPMEMBERSHIP_DATA, selectionArgs);
2338dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2339dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2340a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2341a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
2342a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2343a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
2344a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2345a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2346f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
2347a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
2348de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.clear();
2349de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.putAll(values);
235067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
2351de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
235220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
235324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // If the data being inserted belongs to the user's profile entry, check for the
235424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // WRITE_PROFILE permission before proceeding.
2355afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        enforceProfilePermissionForRawContact(mDb, rawContactId, true);
235624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2357de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace package with internal mapping
2358de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
2359de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (packageName != null) {
2360b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
2361de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
2362de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.RES_PACKAGE);
2363508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2364de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace mimetype with internal mapping
2365de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String mimeType = mValues.getAsString(Data.MIMETYPE);
2366de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (TextUtils.isEmpty(mimeType)) {
2367de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
2368de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
23694097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
2370b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.getMimeTypeId(mimeType));
2371de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
2372a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2373a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
2374d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        id = rowHandler.insert(mDb, mTransactionContext, rawContactId, mValues);
2375f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2376d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            mTransactionContext.markRawContactDirty(rawContactId);
2377a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2378d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.rawContactUpdated(rawContactId);
2379a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
23804f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
23814f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
23823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
23833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Inserts an item in the stream_items table.  The account is checked against the
23843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account in the raw contact for which the stream item is being inserted.  If the
23853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * new stream item results in more stream items under this raw contact than the limit,
23863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the oldest one will be deleted (note that if the stream item inserted was the
23873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * oldest, it will be immediately deleted, and this will return 0).
23883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
23893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param uri the insertion URI
23903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param values the values for the new row
23913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return the stream item _ID of the newly created row, or 0 if it was not created
23923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
23933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long insertStreamItem(Uri uri, ContentValues values) {
23943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long id = 0;
23953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.clear();
23963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.putAll(values);
23973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
23983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long rawContactId = mValues.getAsLong(StreamItems.RAW_CONTACT_ID);
23993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If the data being inserted belongs to the user's profile entry, check for the
24013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // WRITE_PROFILE permission before proceeding.
2402afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        enforceProfilePermissionForRawContact(mDb, rawContactId, true);
24033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Ensure that the raw contact exists and belongs to the caller's account.
24053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, mValues);
24063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccount(account, rawContactId);
24073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24086802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to insert accounts params - they don't exist in the stream items table.
24096802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        mValues.remove(RawContacts.ACCOUNT_NAME);
24106802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        mValues.remove(RawContacts.ACCOUNT_TYPE);
24116802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
24123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Insert the new stream item.
24136802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        id = mDb.insert(Tables.STREAM_ITEMS, null, mValues);
24146802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (id == -1) {
24156802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Insertion failed.
24166802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return 0;
24176802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
24183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check to see if we're over the limit for stream items under this raw contact.
24203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // It's possible that the inserted stream item is older than the the existing
24213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // ones, in which case it may be deleted immediately (resetting the ID to 0).
24223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        id = cleanUpOldStreamItems(rawContactId, id);
24233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return id;
24253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
24263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
24283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Inserts an item in the stream_item_photos table.  The account is checked against
24293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the account in the raw contact that owns the stream item being modified.
24303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
24313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param uri the insertion URI
24323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param values the values for the new row
24336802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @return the stream item photo _ID of the newly created row, or 0 if there was an issue
24346802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     *     with processing the photo or creating the row
24353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
24363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long insertStreamItemPhoto(Uri uri, ContentValues values) {
24373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long id = 0;
24383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.clear();
24393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.putAll(values);
24403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long streamItemId = mValues.getAsLong(StreamItemPhotos.STREAM_ITEM_ID);
24423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        if (streamItemId != 0) {
24433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            long rawContactId = lookupRawContactIdForStreamId(streamItemId);
24443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            // If the data being inserted belongs to the user's profile entry, check for the
24463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            // WRITE_PROFILE permission before proceeding.
2447afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro            enforceProfilePermissionForRawContact(mDb, rawContactId, true);
24483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            // Ensure that the raw contact exists and belongs to the caller's account.
24503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            Account account = resolveAccount(uri, mValues);
24513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            enforceModifyingAccount(account, rawContactId);
24523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24536802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Don't attempt to insert accounts params - they don't exist in the stream item
24546802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // photos table.
24556802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            mValues.remove(RawContacts.ACCOUNT_NAME);
24566802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            mValues.remove(RawContacts.ACCOUNT_TYPE);
24573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24586802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Process the photo and store it.
24596802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            if (processStreamItemPhoto(mValues, false)) {
24606802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // Insert the stream item photo.
24616802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                id = mDb.insert(Tables.STREAM_ITEM_PHOTOS, null, mValues);
24626802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
24633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
24643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return id;
24653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
24663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
24686802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * Processes the photo contained in the {@link ContactsContract.StreamItemPhotos#PHOTO}
24696802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * field of the given values, attempting to store it in the photo store.  If successful,
24706802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * the resulting photo file ID will be added to the values for insert/update in the table.
24716802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * <p>
24726802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * If updating, it is valid for the picture to be empty or unspecified (the function will
24736802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * still return true).  If inserting, a valid picture must be specified.
24746802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @param values The content values provided by the caller.
24756802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @param forUpdate Whether this photo is being processed for update (vs. insert).
24766802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @return Whether the insert or update should proceed.
24776802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     */
24786802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    private boolean processStreamItemPhoto(ContentValues values, boolean forUpdate) {
24796802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (!values.containsKey(StreamItemPhotos.PHOTO)) {
24806802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return forUpdate;
24816802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
24826802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        byte[] photoBytes = values.getAsByteArray(StreamItemPhotos.PHOTO);
24836802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (photoBytes == null) {
24846802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return forUpdate;
24856802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
24866802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
24876802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Process the photo and store it.
24886802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        try {
24896802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            long photoFileId = mPhotoStore.insert(new PhotoProcessor(photoBytes,
24901dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                    mMaxDisplayPhotoDim, mMaxThumbnailPhotoDim, true), true);
24916802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            if (photoFileId != 0) {
24926802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                values.put(StreamItemPhotos.PHOTO_FILE_ID, photoFileId);
24936802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                values.remove(StreamItemPhotos.PHOTO);
24946802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                return true;
24956802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            } else {
24966802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // Couldn't store the photo, return 0.
24976802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                Log.e(TAG, "Could not process stream item photo for insert");
24986802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                return false;
24996802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
25006802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        } catch (IOException ioe) {
25016802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            Log.e(TAG, "Could not process stream item photo for insert", ioe);
25026802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return false;
25036802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
25046802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    }
25056802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
25066802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    /**
25073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Looks up the raw contact ID that owns the specified stream item.
25083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param streamItemId The ID of the stream item.
25093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The associated raw contact ID, or -1 if no such stream item exists.
25103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
25113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long lookupRawContactIdForStreamId(long streamItemId) {
25123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long rawContactId = -1;
25133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c = mDb.query(Tables.STREAM_ITEMS, new String[]{StreamItems.RAW_CONTACT_ID},
25143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                StreamItems._ID + "=?", new String[]{String.valueOf(streamItemId)},
25153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                null, null, null);
25163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
25173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if (c.moveToFirst()) {
25183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                rawContactId = c.getLong(0);
25193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
25203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
25213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
25223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
25233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return rawContactId;
25243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
25253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
25263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
25273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given raw contact ID is owned by the given account.
25283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * If the resolved account is null, this will return true iff the raw contact
25293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * is also associated with the "null" account.
25303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
25313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * If the resolved account does not match, this will throw a security exception.
25323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
25333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param rawContactId The raw contact ID to check for.
25343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
25353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void enforceModifyingAccount(Account account, long rawContactId) {
25363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        String accountSelection = RawContactsColumns.CONCRETE_ID + "=? AND "
25373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_NAME + "=? AND "
25383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + "=?";
25393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        String noAccountSelection = RawContactsColumns.CONCRETE_ID + "=? AND "
25403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " IS NULL AND "
25413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " IS NULL";
25423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c;
25433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        if (account != null) {
25443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContactsColumns.CONCRETE_ID},
25453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    accountSelection,
25463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    new String[]{String.valueOf(rawContactId), mAccount.name, mAccount.type},
25473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    null, null, null);
25483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } else {
25493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContactsColumns.CONCRETE_ID},
25503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    noAccountSelection, new String[]{String.valueOf(rawContactId)},
25513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    null, null, null);
25523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
25533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
25543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if(c.getCount() == 0) {
25553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                throw new SecurityException("Caller account does not match raw contact ID "
25563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    + rawContactId);
25573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
25583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
25593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
25603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
25613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
25623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
25633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
25643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given selection of stream items matches up with the given
25653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account.  If any of the raw contacts fail the account check, this will throw a
25663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * security exception.
25673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
25683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selection The selection.
25693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selectionArgs The selection arguments.
25703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The list of stream item IDs that would be included in this selection.
25713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
25723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private List<Long> enforceModifyingAccountForStreamItems(Account account, String selection,
25733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
25743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemIds = Lists.newArrayList();
25753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
25763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        setTablesAndProjectionMapForStreamItems(qb);
25773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c = qb.query(mDb,
25783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{StreamItems._ID, StreamItems.RAW_CONTACT_ID},
25793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selection, selectionArgs, null, null, null);
25803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
25813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            while (c.moveToNext()) {
25823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                streamItemIds.add(c.getLong(0));
25833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
25843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Throw a security exception if the account doesn't match the raw contact's.
25853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                enforceModifyingAccount(account, c.getLong(1));
25863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
25873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
25883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
25893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
25903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemIds;
25913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
25923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
25933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
25943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given selection of stream item photos matches up with the given
25953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account.  If any of the raw contacts fail the account check, this will throw a
25963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * security exception.
25973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
25983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selection The selection.
25993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selectionArgs The selection arguments.
26003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The list of stream item photo IDs that would be included in this selection.
26013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
26023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private List<Long> enforceModifyingAccountForStreamItemPhotos(Account account, String selection,
26033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
26043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemPhotoIds = Lists.newArrayList();
26053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
26063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        setTablesAndProjectionMapForStreamItemPhotos(qb);
26073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c = qb.query(mDb, new String[]{StreamItemPhotos._ID, StreamItems.RAW_CONTACT_ID},
26083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selection, selectionArgs, null, null, null);
26093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
26103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            while (c.moveToNext()) {
26113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                streamItemPhotoIds.add(c.getLong(0));
26123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
26133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Throw a security exception if the account doesn't match the raw contact's.
26143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                enforceModifyingAccount(account, c.getLong(1));
26153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
26163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
26173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
26183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
26193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemPhotoIds;
26203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
26213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
26223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
26233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Queries the database for stream items under the given raw contact.  If there are
26243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * more entries than {@link ContactsProvider2#MAX_STREAM_ITEMS_PER_RAW_CONTACT},
26253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the oldest entries (as determined by timestamp) will be deleted.
26263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param rawContactId The raw contact ID to examine for stream items.
26273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param insertedStreamItemId The ID of the stream item that was just inserted,
26283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     prompting this cleanup.  Callers may pass 0 if no insertion prompted the
26293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     cleanup.
26303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The ID of the inserted stream item if it still exists after cleanup;
26313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     0 otherwise.
26323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
26333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long cleanUpOldStreamItems(long rawContactId, long insertedStreamItemId) {
26343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long postCleanupInsertedStreamId = insertedStreamItemId;
26353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c = mDb.query(Tables.STREAM_ITEMS, new String[]{StreamItems._ID},
26363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                StreamItems.RAW_CONTACT_ID + "=?", new String[]{String.valueOf(rawContactId)},
26373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                null, null, StreamItems.TIMESTAMP + " DESC, " + StreamItems._ID + " DESC");
26383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
26393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            int streamItemCount = c.getCount();
26403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if (streamItemCount <= MAX_STREAM_ITEMS_PER_RAW_CONTACT) {
26413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Still under the limit - nothing to clean up!
26423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return insertedStreamItemId;
26433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            } else {
26443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                c.moveToLast();
26453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                while (c.getPosition() >= MAX_STREAM_ITEMS_PER_RAW_CONTACT) {
26463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    long streamItemId = c.getLong(0);
26473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    if (insertedStreamItemId == streamItemId) {
26483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        // The stream item just inserted is being deleted.
26493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        postCleanupInsertedStreamId = 0;
26503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    }
26513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    deleteStreamItem(c.getLong(0));
26523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    c.moveToPrevious();
26533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
26543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
26553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
26563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
26573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
26583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return postCleanupInsertedStreamId;
26593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
26603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
2661ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
26628ab0b7a48efe540226253567bcf6fdbc487186a2Dmitri Plotnikov        mDbHelper.updateRawContactDisplayName(db, rawContactId);
2663d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    }
2664d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
26659261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
266620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
266720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
2668f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
266920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
267020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2671de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
2672de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
2673f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataRowHandler.DataDeleteQuery.COLUMNS,
2674f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                selection, selectionArgs, null);
2675de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        try {
2676de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            while(c.moveToNext()) {
2677f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                long rawContactId = c.getLong(DataRowHandler.DataDeleteQuery.RAW_CONTACT_ID);
267824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
267924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // Check for write profile permission if the data belongs to the profile.
2680afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForRawContact(mDb, rawContactId, true);
268124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2682f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
2683a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                DataRowHandler rowHandler = getDataRowHandler(mimeType);
2684d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                count += rowHandler.delete(mDb, mTransactionContext, c);
2685f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                if (!callerIsSyncAdapter) {
2686d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                    mTransactionContext.markRawContactDirty(rawContactId);
268788e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                }
268820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
268920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
2690de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            c.close();
269120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
269220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
269320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
269420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
269520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
269688e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    /**
269788e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     * Delete a data row provided that it is one of the allowed mime types.
269888e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     */
269920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
2700f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
270188e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
270288e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
27034da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(dataId);
2704f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataRowHandler.DataDeleteQuery.COLUMNS, Data._ID + "=?",
27054da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1, null);
2706f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
270720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
270820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
270920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
271020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
271120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2712f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov            String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
271320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
271420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
271520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
271620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
271720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
271820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
271920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
272020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
272120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
27227a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException("Data type mismatch: expected "
272320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
272420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
272520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
272624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Check for write profile permission if the data belongs to the profile.
272724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            long rawContactId = c.getLong(DataRowHandler.DataDeleteQuery.RAW_CONTACT_ID);
2728afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro            enforceProfilePermissionForRawContact(mDb, rawContactId, true);
272924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2730a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            DataRowHandler rowHandler = getDataRowHandler(mimeType);
2731d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            return rowHandler.delete(mDb, mTransactionContext, c);
273220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
273320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
273420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
273520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
273620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
273720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
2738ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
2739ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
2740f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private long insertGroup(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
2741f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2742f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2743f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2744e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final Account account = resolveAccount(uri, mValues);
2745ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2746ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
2747f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String packageName = mValues.getAsString(Groups.RES_PACKAGE);
274867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
2749f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(GroupsColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
275067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
2751f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.remove(Groups.RES_PACKAGE);
2752ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2753dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final boolean isFavoritesGroup = mValues.getAsLong(Groups.FAVORITES) != null
2754dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                ? mValues.getAsLong(Groups.FAVORITES) != 0
2755dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                : false;
2756dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2757f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2758f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(Groups.DIRTY, 1);
275973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
276073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2761f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long result = mDb.insert(Tables.GROUPS, Groups.TITLE, mValues);
2762ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2763dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter && isFavoritesGroup) {
2764dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // add all starred raw contacts to this group
2765dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String selection;
2766dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs;
2767dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (account == null) {
2768dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + " IS NULL AND "
2769dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + RawContacts.ACCOUNT_TYPE + " IS NULL";
2770dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = null;
2771dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
2772dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + "=? AND "
2773dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + RawContacts.ACCOUNT_TYPE + "=?";
2774dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = new String[]{account.name, account.type};
2775dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2776dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            Cursor c = mDb.query(Tables.RAW_CONTACTS,
2777dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[]{RawContacts._ID, RawContacts.STARRED},
2778dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    selection, selectionArgs, null, null, null);
2779892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            try {
2780892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                while (c.moveToNext()) {
2781892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (c.getLong(1) != 0) {
2782892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        final long rawContactId = c.getLong(0);
2783892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        insertDataGroupMembership(rawContactId, result);
2784d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                        mTransactionContext.markRawContactDirty(rawContactId);
2785892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    }
2786dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
2787892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            } finally {
2788892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                c.close();
2789dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2790dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2791dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2792f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mValues.containsKey(Groups.GROUP_VISIBLE)) {
27931a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2794ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        }
2795ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2796ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        return result;
2797ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2798ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
27995aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private long insertSettings(Uri uri, ContentValues values) {
2800e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final long id = mDb.insert(Tables.SETTINGS, null, values);
28015aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
28021a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
28031a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2804e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
28051a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
2806e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return id;
2807e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2808e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2809ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
281082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov     * Inserts a status update.
28111f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
281282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    public long insertStatusUpdate(ContentValues values) {
281382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        final String handle = values.getAsString(StatusUpdates.IM_HANDLE);
28140a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        final Integer protocol = values.getAsInteger(StatusUpdates.PROTOCOL);
28154dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        String customProtocol = null;
28164dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
28170a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        if (protocol != null && protocol == Im.PROTOCOL_CUSTOM) {
281882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            customProtocol = values.getAsString(StatusUpdates.CUSTOM_PROTOCOL);
28194dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (TextUtils.isEmpty(customProtocol)) {
28204dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                throw new IllegalArgumentException(
28214dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                        "CUSTOM_PROTOCOL is required when PROTOCOL=PROTOCOL_CUSTOM");
28224dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
28231f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
28241f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2825dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long rawContactId = -1;
2826dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long contactId = -1;
282782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Long dataId = values.getAsLong(StatusUpdates.DATA_ID);
28286802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        String accountType = null;
28296802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        String accountName = null;
2830f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.setLength(0);
28312526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov        mSelectionArgs.clear();
2832dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        if (dataId != null) {
2833dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the contact info for the given data row.
2834dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
28352526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSb.append(Tables.DATA + "." + Data._ID + "=?");
28362526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSelectionArgs.add(String.valueOf(dataId));
28371f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
2838dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the data row to attach this presence update to
2839dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
28400a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(handle) || protocol == null) {
28410a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                throw new IllegalArgumentException("PROTOCOL and IM_HANDLE are required");
28420a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
28430a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2844dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // TODO: generalize to allow other providers to match against email
2845dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == protocol;
2846dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
28472a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov            String mimeTypeIdIm = String.valueOf(mDbHelper.getMimeTypeIdForIm());
2848dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            if (matchEmail) {
28492a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                String mimeTypeIdEmail = String.valueOf(mDbHelper.getMimeTypeIdForEmail());
2850f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
2851f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // The following hack forces SQLite to use the (mimetype_id,data1) index, otherwise
2852f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the "OR" conjunction confuses it and it switches to a full scan of
2853f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the raw_contacts table.
2854f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
2855f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // This code relies on the fact that Im.DATA and Email.DATA are in fact the same
2856f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // column - Data.DATA1
28572526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + " IN (?,?)" +
28582526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Data.DATA1 + "=?" +
28592526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND ((" + DataColumns.MIMETYPE_ID + "=? AND " + Im.PROTOCOL + "=?");
28602526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
28612526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
28622526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
28632526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
28642526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
2865dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
28662526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
28672526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
2868dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
28692526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(") OR (" + DataColumns.MIMETYPE_ID + "=?))");
28702526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
2871dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            } else {
28722526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + "=?" +
28732526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.PROTOCOL + "=?" +
28742526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.DATA + "=?");
28752526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
28762526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
28772526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
2878dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
28792526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
28802526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
2881dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
2882dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
28831f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
288482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.DATA_ID)) {
28852526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(" AND " + DataColumns.CONCRETE_ID + "=?");
28862526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(values.getAsString(StatusUpdates.DATA_ID));
2887dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
288870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
288970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
28901f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
28911f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
2892de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            cursor = mDb.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
28932526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.toString(), mSelectionArgs.toArray(EMPTY_STRING_ARRAY), null, null,
28944394086494fe7909aaca70f56fb4bb08beebf303Dmitri Plotnikov                    Clauses.CONTACT_VISIBLE + " DESC, " + Data.RAW_CONTACT_ID);
28951f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
289667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
28975ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
28986802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                accountType = cursor.getString(DataContactsQuery.ACCOUNT_TYPE);
28996802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                accountName = cursor.getString(DataContactsQuery.ACCOUNT_NAME);
2900e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
29011f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
29021f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
29031f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
29041f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
29051f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
290631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
290731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
290831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
29091f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
29101f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
291182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.PRESENCE)) {
2912a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (customProtocol == null) {
2913a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // We cannot allow a null in the custom protocol field, because SQLite3 does not
2914a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // properly enforce uniqueness of null values
2915a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                customProtocol = "";
2916a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
2917a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2918a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.clear();
291982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.DATA_ID, dataId);
2920a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.RAW_CONTACT_ID, rawContactId);
2921a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.CONTACT_ID, contactId);
292282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PROTOCOL, protocol);
292382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
292482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.IM_HANDLE, handle);
292582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.IM_ACCOUNT)) {
292682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                mValues.put(StatusUpdates.IM_ACCOUNT, values.getAsString(StatusUpdates.IM_ACCOUNT));
2927a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
292882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PRESENCE,
292982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    values.getAsString(StatusUpdates.PRESENCE));
2930aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori            mValues.put(StatusUpdates.CHAT_CAPABILITY,
2931aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                    values.getAsString(StatusUpdates.CHAT_CAPABILITY));
29321f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2933a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            // Insert the presence update
2934a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mDb.replace(Tables.PRESENCE, null, mValues);
2935a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
2936e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
29370a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
293882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.STATUS)) {
293982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String status = values.getAsString(StatusUpdates.STATUS);
29400a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String resPackage = values.getAsString(StatusUpdates.STATUS_RES_PACKAGE);
29410a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Integer labelResource = values.getAsInteger(StatusUpdates.STATUS_LABEL);
29420a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
29430a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(resPackage)
29440a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && (labelResource == null || labelResource == 0)
29450a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && protocol != null) {
29460a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                labelResource = Im.getProtocolLabelResource(protocol);
29470a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
29480a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
29490a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Long iconResource = values.getAsLong(StatusUpdates.STATUS_ICON);
29500a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            // TODO compute the default icon based on the protocol
29510a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2952a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (TextUtils.isEmpty(status)) {
295378fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                mDbHelper.deleteStatusUpdate(dataId);
2954a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            } else {
29556802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                Long timestamp = values.getAsLong(StatusUpdates.STATUS_TIMESTAMP);
29566802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (timestamp != null) {
29576802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    mDbHelper.replaceStatusUpdate(dataId, timestamp, status, resPackage,
29586802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            iconResource, labelResource);
29596802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                } else {
29606802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    mDbHelper.insertStatusUpdate(dataId, status, resPackage, iconResource,
29616802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            labelResource);
29626802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                }
29636802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
29646802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // For forward compatibility with the new stream item API, insert this status update
29656802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // there as well.  If we already have a stream item from this source, update that
29666802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // one instead of inserting a new one (since the semantics of the old status update
29676802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // API is to only have a single record).
29686802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (rawContactId != -1 && !TextUtils.isEmpty(status)) {
29696802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    ContentValues streamItemValues = new ContentValues();
29706802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RAW_CONTACT_ID, rawContactId);
29716802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.TEXT, status);
29726802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.COMMENTS, "");
29736802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_PACKAGE, resPackage);
29746802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_ICON, iconResource);
29756802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_LABEL, labelResource);
29766802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.TIMESTAMP,
29776802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            timestamp == null ? System.currentTimeMillis() : timestamp);
29786802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
29796802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // Note: The following is basically a workaround for the fact that status
29806802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // updates didn't do any sort of account enforcement, while social stream item
29816802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // updates do.  We can't expect callers of the old API to start passing account
29826802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // information along, so we just populate the account params appropriately for
29836802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // the raw contact.
29846802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    if (accountName != null && accountType != null) {
29856802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        streamItemValues.put(RawContacts.ACCOUNT_NAME, accountName);
29866802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        streamItemValues.put(RawContacts.ACCOUNT_TYPE, accountType);
29876802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    }
29886802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
29896802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // Check for an existing stream item from this source, and insert or update.
29906802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    Uri streamUri = StreamItems.CONTENT_URI;
29916802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    Cursor c = query(streamUri, new String[]{StreamItems._ID},
29926802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            StreamItems.RAW_CONTACT_ID + "=?",
29936802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            new String[]{String.valueOf(rawContactId)}, null);
29946802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    try {
29956802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        if (c.getCount() > 0) {
29966802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            c.moveToFirst();
29976802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            update(ContentUris.withAppendedId(streamUri, c.getLong(0)),
29986802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    streamItemValues, null, null);
29996802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        } else {
30006802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            insert(streamUri, streamItemValues);
30016802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        }
30026802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    } finally {
30036802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        c.close();
30046802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    }
30056802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
30066802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                }
3007e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            }
3008e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        }
3009bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
3010a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (contactId != -1) {
3011f4015ab9ab7c26b766b5331fbf6655b8c54877eaDmitri Plotnikov            mContactAggregator.updateLastStatusUpdateId(contactId);
3012a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3013a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3014a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        return dataId;
30151f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
30161f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
30174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3018de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
3019bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3020b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "deleteInTransaction: " + uri);
3021b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3022b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3023f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3024f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
3025508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
3026508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
302735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3028b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs);
302935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3030b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID:
3031b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3032b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3033b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
3034b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selectionWithId, selectionArgs);
3035b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3036cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            case CONTACTS: {
3037cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // TODO
3038cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return 0;
3039cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3040cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3041d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
3042d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3043dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
30446bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
30456bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
30469fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP: {
30472e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
30482e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
30492e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
3050fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
3051fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
30522e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
30532e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
30542e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
3055dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
30562e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
30572e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
30589fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP_ID: {
30599fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                // lookup contact by id and lookup key to see if they still match the actual record
30609fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final List<String> pathSegments = uri.getPathSegments();
30619fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final String lookupKey = pathSegments.get(2);
30629fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
30639fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                setTablesAndProjectionMapForContacts(lookupQb, uri, null);
3064a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
30659fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                String[] args;
30669fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                if (selectionArgs == null) {
30679fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[2];
30689fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } else {
30699fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[selectionArgs.length + 2];
30709fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
30719fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
30729fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                args[0] = String.valueOf(contactId);
307360de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann                args[1] = Uri.encode(lookupKey);
30749fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                lookupQb.appendWhere(Contacts._ID + "=? AND " + Contacts.LOOKUP_KEY + "=?");
30759fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final SQLiteDatabase db = mDbHelper.getReadableDatabase();
30769fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                Cursor c = query(db, lookupQb, null, selection, args, null, null, null);
30779fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                try {
30789fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    if (c.getCount() == 1) {
30799fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // contact was unmodified so go ahead and delete it
3080dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        return deleteContact(contactId, callerIsSyncAdapter);
30819fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    } else {
30829fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // row was changed (e.g. the merging might have changed), we got multiple
30839fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // rows or the supplied selection filtered the record out
30849fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        return 0;
30859fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    }
30869fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } finally {
30879fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    c.close();
30889fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
30899fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            }
30909fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
30912971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case RAW_CONTACTS: {
30922971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
3093fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                Cursor c = mDb.query(Tables.RAW_CONTACTS,
3094fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        new String[]{RawContacts._ID, RawContacts.CONTACT_ID},
3095e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
30962971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
30972971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
30982971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                        final long rawContactId = c.getLong(0);
3099fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        long contactId = c.getLong(1);
3100fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        numDeletes += deleteRawContact(rawContactId, contactId,
3101fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                                callerIsSyncAdapter);
31022971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
31032971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
31042971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
31052971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
31062971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
31072971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
31082971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
31095ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
31102971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                final long rawContactId = ContentUris.parseId(uri);
3111fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                return deleteRawContact(rawContactId, mDbHelper.getContactId(rawContactId),
3112fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        callerIsSyncAdapter);
3113508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3114508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
311520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
3116f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
3117944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                return deleteData(appendAccountToSelection(uri, selection), selectionArgs,
3118f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        callerIsSyncAdapter);
311920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
312020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
312148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
312248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
312348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
312448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
3125508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
3126f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
31274da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
31284da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                return deleteData(Data._ID + "=?", mSelectionArgs1, callerIsSyncAdapter);
3129ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3130ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3131ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3132f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
31335aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteGroup(uri, ContentUris.parseId(uri), callerIsSyncAdapter);
31342971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
31352971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
31362971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case GROUPS: {
31372971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
31382971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups._ID},
3139e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
31402971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
31412971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
31425aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        numDeletes += deleteGroup(uri, c.getLong(0), callerIsSyncAdapter);
31432971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
31442971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
31452971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
31462971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
314781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (numDeletes > 0) {
3148f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
314981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
31502971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
3151508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3152508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
3153eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
315443880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3155e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                return deleteSettings(uri, appendAccountToSelection(uri, selection), selectionArgs);
3156eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3157eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
315882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
31590a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                return deleteStatusUpdates(selection, selectionArgs);
31601f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
31611f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
31623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
31633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
31643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItems(uri, new ContentValues(), selection, selectionArgs);
31653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
31663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
31673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
31683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
31693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItems(uri, new ContentValues(),
31703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemsColumns.CONCRETE_ID + "=?",
31713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{uri.getLastPathSegment()});
31723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
31733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
31743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
31753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
31763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItemPhotos(uri, new ContentValues(), selection, selectionArgs);
31773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
31783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
31793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
31803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
31813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
31823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
31833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItemPhotos(uri, new ContentValues(),
31843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=? AND "
31853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                                + StreamItemPhotos.STREAM_ITEM_ID + "=?",
31863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{streamItemPhotoId, streamItemId});
31873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
31883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
318981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
319081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
31913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
319281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
3193508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
31944f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
31954f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
31961c8e40c18f92722b9bec6e8ce2e345a9828efa16Dmitri Plotnikov    public int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) {
3197ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
3198b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final long groupMembershipMimetypeId = mDbHelper
319994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
3200de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mDb.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
320194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
320294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupId, null);
320394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
320494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        try {
3205f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (callerIsSyncAdapter) {
3206de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
320794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            } else {
320894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.clear();
320994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.put(Groups.DELETED, 1);
3210f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mValues.put(Groups.DIRTY, 1);
3211de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId, null);
321294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            }
321394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        } finally {
32141a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
321594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
321694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
321794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
32185aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
3219e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.delete(Tables.SETTINGS, selection, selectionArgs);
32201a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        mVisibleTouched = true;
3221e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3222e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3223e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3224dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int deleteContact(long contactId, boolean callerIsSyncAdapter) {
3225afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        enforceProfilePermissionForContact(mDb, contactId, true);
322696b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(contactId);
3227cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
322896b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                RawContacts.CONTACT_ID + "=?", mSelectionArgs1,
322996b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                null, null, null);
3230cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        try {
3231cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            while (c.moveToNext()) {
3232cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                long rawContactId = c.getLong(0);
3233dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
3234cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3235cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } finally {
3236cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            c.close();
3237cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
3238cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
32393826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
32403826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
3241cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return mDb.delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
3242cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3243cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3244fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov    public int deleteRawContact(long rawContactId, long contactId, boolean callerIsSyncAdapter) {
3245afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        enforceProfilePermissionForRawContact(mDb, rawContactId, true);
32463389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
32473826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
32483826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
3249f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (callerIsSyncAdapter) {
325014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            mDb.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
3251fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            int count = mDb.delete(Tables.RAW_CONTACTS, RawContacts._ID + "=" + rawContactId, null);
3252fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            mContactAggregator.updateDisplayNameForContact(mDb, contactId);
3253fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return count;
325433b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } else {
3255b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.removeContactIfSingleton(rawContactId);
3256dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
325733b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        }
325833b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
325933b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
32600a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private int deleteStatusUpdates(String selection, String[] selectionArgs) {
32619705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // delete from both tables: presence and status_updates
32629705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // TODO should account type/name be appended to the where clause?
32639705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      if (VERBOSE_LOGGING) {
32649705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          Log.v(TAG, "deleting data from status_updates for " + selection);
32659705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      }
32669705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      mDb.delete(Tables.STATUS_UPDATES, getWhereClauseForStatusUpdatesTable(selection),
32679705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          selectionArgs);
32689705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      return mDb.delete(Tables.PRESENCE, selection, selectionArgs);
32690a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
32700a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
32713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItems(Uri uri, ContentValues values, String selection,
32723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
32733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // First query for the stream items to be deleted, and check that they belong
32743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // to the account.
32753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
32763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemIds = enforceModifyingAccountForStreamItems(
32773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                account, selection, selectionArgs);
32783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
32793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If no security exception has been thrown, we're fine to delete.
32803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        for (long streamItemId : streamItemIds) {
32813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            deleteStreamItem(streamItemId);
32823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
32833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
32843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mVisibleTouched = true;
32853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemIds.size();
32863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
32873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
32883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItem(long streamItemId) {
32893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Note that this does not enforce the modifying account.
32903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        deleteStreamItemPhotos(streamItemId);
32913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return mDb.delete(Tables.STREAM_ITEMS, StreamItems._ID + "=?",
32923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{String.valueOf(streamItemId)});
32933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
32943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
32953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItemPhotos(Uri uri, ContentValues values, String selection,
32963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
32973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // First query for the stream item photos to be deleted, and check that they
32983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // belong to the account.
32993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
33003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItemPhotos(account, selection, selectionArgs);
33013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
33023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If no security exception has been thrown, we're fine to delete.
33033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return mDb.delete(Tables.STREAM_ITEM_PHOTOS, selection, selectionArgs);
33043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
33053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
33063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItemPhotos(long streamItemId) {
33073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Note that this does not enforce the modifying account.
33083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return mDb.delete(Tables.STREAM_ITEM_PHOTOS, StreamItemPhotos.STREAM_ITEM_ID + "=?",
33093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{String.valueOf(streamItemId)});
33103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
33113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
3312dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int markRawContactAsDeleted(long rawContactId, boolean callerIsSyncAdapter) {
331381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = true;
331481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
3315cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.clear();
3316cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DELETED, 1);
3317cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
3318cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
3319cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
3320cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DIRTY, 1);
3321dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        return updateRawContact(rawContactId, mValues, callerIsSyncAdapter);
3322cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3323cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
33244f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3325de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
3326de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            String[] selectionArgs) {
3327bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3328b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "updateInTransaction: " + uri);
3329b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3330b5a4add17815167d20a90645779df34cdf45280dFred Quintana
333135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
333200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
333300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
3334b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (match == SYNCSTATE_ID && selection == null) {
3335b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long rowId = ContentUris.parseId(uri);
33361129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Object data = values.get(ContactsContract.SyncState.DATA);
3337d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            mTransactionContext.syncStateUpdated(rowId, data);
3338b5a4add17815167d20a90645779df34cdf45280dFred Quintana            return 1;
3339b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3340b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3341f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3342f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
334300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
334435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3345b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
3346b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        appendAccountToSelection(uri, selection), selectionArgs);
3347b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3348b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID: {
3349b5a4add17815167d20a90645779df34cdf45280dFred Quintana                selection = appendAccountToSelection(uri, selection);
3350b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3351b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3352b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
3353b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
3354b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        selectionWithId, selectionArgs);
3355b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
335635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3357d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
3358dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(values, selection, selectionArgs, callerIsSyncAdapter);
335900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
336000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
336100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3362d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
3363dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(ContentUris.parseId(uri), values, callerIsSyncAdapter);
3364c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
3365c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
3366c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
336724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
336824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // Restrict update to the user's profile.
336924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                StringBuilder profileSelection = new StringBuilder();
337024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                profileSelection.append(Contacts.IS_USER_PROFILE + "=1");
337124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                if (!TextUtils.isEmpty(selection)) {
337224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    profileSelection.append(" AND (").append(selection).append(")");
337324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                }
337424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                count = updateContactOptions(values, profileSelection.toString(), selectionArgs,
337524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        callerIsSyncAdapter);
337624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
337724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
337824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
33792e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
33802e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
33812e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
33822e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
33832e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
3384fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
3385fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
33862e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
33872e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
33882e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
3389dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(contactId, values, callerIsSyncAdapter);
33902e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                break;
33912e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
33922e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
33937d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            case RAW_CONTACTS_DATA: {
33947d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                final String rawContactId = uri.getPathSegments().get(1);
33957d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
33967d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                    + (selection == null ? "" : " AND " + selection);
33977d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
33987d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter);
33997d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
34007d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                break;
34017d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            }
34027d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
340320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
3404944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                count = updateData(uri, values, appendAccountToSelection(uri, selection),
3405f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
340681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3407f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
340881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
340920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
341020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3411c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
341248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
341348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
341448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
341548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
3416f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
341781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3418f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
341981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
342000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
342100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
34227e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
34235ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
34245ac5c70d1165309302ebcc931f51723e37d31e0bJeff Sharkey                selection = appendAccountToSelection(uri, selection);
3425dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateRawContacts(values, selection, selectionArgs, callerIsSyncAdapter);
34267e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
34277e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
34287e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
34295ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
343033b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
34314529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                if (selection != null) {
34324da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
34334da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=?"
3434dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                                    + " AND(" + selection + ")", selectionArgs,
3435dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
34364529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                } else {
34374da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(rawContactId);
3438dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    count = updateRawContacts(values, RawContacts._ID + "=?", mSelectionArgs1,
3439dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
34404529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                }
34417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
34427e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
34437e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
3444ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
34455aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, appendAccountToSelection(uri, selection),
3446f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
344781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3448f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
344981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3450ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3451ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3452ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3453ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3454ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
34554da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(groupId));
34564da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                String selectionWithId = Groups._ID + "=? "
345773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                        + (selection == null ? "" : " AND " + selection);
34585aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, selectionWithId, selectionArgs,
34595aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        callerIsSyncAdapter);
346081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3461f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
346281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3463ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3464ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3465ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3466127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
3467de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                count = updateAggregationException(mDb, values);
3468b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
3469b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
3470b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
3471eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
3472e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                count = updateSettings(uri, values, appendAccountToSelection(uri, selection),
3473e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        selectionArgs);
347443880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3475eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
3476eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3477eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
34789705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            case STATUS_UPDATES: {
34799705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                count = updateStatusUpdate(uri, values, selection, selectionArgs);
34809705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                break;
34819705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            }
34829705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
34833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
34843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItems(uri, values, selection, selectionArgs);
34853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
34863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
34873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
34883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
34893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItems(uri, values, StreamItemsColumns.CONCRETE_ID + "=?",
34903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{uri.getLastPathSegment()});
34913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
34923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
34933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
34943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
34953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values, selection, selectionArgs);
34963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
34973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
34983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
34993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
35003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
35013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values,
35023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotos.STREAM_ITEM_ID + "=?", new String[]{streamItemId});
35033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
35043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
35053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
35063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
35073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
35083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
35093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values,
35103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=? AND " +
35113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                                StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=?",
35123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{streamItemPhotoId, streamItemId});
35133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
35143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
35153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
351672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            case DIRECTORIES: {
3517bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                mContactDirectoryManager.scanPackagesByUid(Binder.getCallingUid());
351872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                count = 1;
3519d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
3520d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
3521d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
352246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            case DATA_USAGE_FEEDBACK_ID: {
352346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (handleDataUsageFeedback(uri)) {
352446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    count = 1;
352546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                } else {
352646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    count = 0;
352746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
352846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                break;
352946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
353046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
353181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
353281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
3533f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
353481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
353500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
353600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
353700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
35384f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
35394f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
35409705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private int updateStatusUpdate(Uri uri, ContentValues values, String selection,
35419705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        String[] selectionArgs) {
35429705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // update status_updates table, if status is provided
35439705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO should account type/name be appended to the where clause?
35449705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        int updateCount = 0;
35459705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContentValues settableValues = getSettableColumnsForStatusUpdatesTable(values);
35469705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
35479705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.STATUS_UPDATES,
35489705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    settableValues,
35499705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    getWhereClauseForStatusUpdatesTable(selection),
35509705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selectionArgs);
35519705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
35529705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
35539705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // now update the Presence table
35549705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        settableValues = getSettableColumnsForPresenceTable(values);
35559705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
35569705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.PRESENCE, settableValues,
35579705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selection, selectionArgs);
35589705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
35599705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO updateCount is not entirely a valid count of updated rows because 2 tables could
35609705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // potentially get updated in this method.
35619705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return updateCount;
35629705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
35639705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
35643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int updateStreamItems(Uri uri, ContentValues values, String selection,
35653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
35663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Stream items can't be moved to a new raw contact.
35673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        values.remove(StreamItems.RAW_CONTACT_ID);
35683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
35693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check that the stream items being updated belong to the account.
35703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
35713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItems(account, selection, selectionArgs);
35723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
35736802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to update accounts params - they don't exist in the stream items table.
35746802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_NAME);
35756802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_TYPE);
35766802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
35773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If there's been no exception, the update should be fine.
35783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return mDb.update(Tables.STREAM_ITEMS, values, selection, selectionArgs);
35793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
35803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
35813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int updateStreamItemPhotos(Uri uri, ContentValues values, String selection,
35823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
35833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Stream item photos can't be moved to a new stream item.
35843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        values.remove(StreamItemPhotos.STREAM_ITEM_ID);
35853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
35863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check that the stream item photos being updated belong to the account.
35873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
35883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItemPhotos(account, selection, selectionArgs);
35893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
35906802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to update accounts params - they don't exist in the stream item
35916802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // photos table.
35926802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_NAME);
35936802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_TYPE);
35946802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
35956802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Process the photo (since we're updating, it's valid for the photo to not be present).
35966802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (processStreamItemPhoto(values, true)) {
35976802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // If there's been no exception, the update should be fine.
35986802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return mDb.update(Tables.STREAM_ITEM_PHOTOS, values, selection, selectionArgs);
35996802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
36006802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        return 0;
36013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
36023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
36039705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    /**
36049705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     * Build a where clause to select the rows to be updated in status_updates table.
36059705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     */
36069705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private String getWhereClauseForStatusUpdatesTable(String selection) {
36079705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.setLength(0);
36089705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE);
36099705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(selection);
36109705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(")");
36119705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mSb.toString();
36129705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
36139705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
36149705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForStatusUpdatesTable(ContentValues values) {
36159705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
36169705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS, values,
36179705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS);
36189705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_TIMESTAMP, values,
36199705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_TIMESTAMP);
36209705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_RES_PACKAGE, values,
36219705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_RES_PACKAGE);
36229705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_LABEL, values,
36239705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_LABEL);
36249705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_ICON, values,
36259705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_ICON);
36269705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
36279705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
36289705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
36299705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForPresenceTable(ContentValues values) {
36309705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
36319705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.PRESENCE, values,
36329705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.PRESENCE);
3633aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.CHAT_CAPABILITY, values,
3634aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                StatusUpdates.CHAT_CAPABILITY);
36359705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
36369705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
36379705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
36385aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int updateGroups(Uri uri, ContentValues values, String selectionWithId,
3639f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
364073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3641ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
3642ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
364373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        ContentValues updatedValues;
3644f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter && !values.containsKey(Groups.DIRTY)) {
364573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = mValues;
364673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.clear();
364773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.putAll(values);
364873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.put(Groups.DIRTY, 1);
364973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } else {
365073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = values;
365173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
365273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3653ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        int count = mDb.update(Tables.GROUPS, updatedValues, selectionWithId, selectionArgs);
36541a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) {
36551a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
365694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
36576ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        if (updatedValues.containsKey(Groups.SHOULD_SYNC)
36581129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                && updatedValues.getAsInteger(Groups.SHOULD_SYNC) != 0) {
36596ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups.ACCOUNT_NAME,
3660e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    Groups.ACCOUNT_TYPE}, selectionWithId, selectionArgs, null,
36616ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    null, null);
36626ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountName;
36636ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountType;
36646ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            try {
36656ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                while (c.moveToNext()) {
36666ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountName = c.getString(0);
36676ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountType = c.getString(1);
366824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
36696ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        Account account = new Account(accountName, accountType);
3670ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        ContentResolver.requestSync(account, ContactsContract.AUTHORITY,
36716ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                                new Bundle());
36726ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        break;
36736ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    }
36746ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                }
36756ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            } finally {
36766ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                c.close();
36776ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            }
36786ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        }
367994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        return count;
368094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
368194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
3682b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private int updateSettings(Uri uri, ContentValues values, String selection,
3683b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            String[] selectionArgs) {
3684e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.update(Tables.SETTINGS, values, selection, selectionArgs);
36851a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
36861a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3687e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
3688e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3689e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3690e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3691dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContacts(ContentValues values, String selection, String[] selectionArgs,
3692dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
36934529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (values.containsKey(RawContacts.CONTACT_ID)) {
36944529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            throw new IllegalArgumentException(RawContacts.CONTACT_ID + " should not be included " +
36954529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    "in content values. Contact IDs are assigned automatically");
36964529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
369773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
369897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
369997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
370097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0");
370197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
370297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
37034529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = 0;
3704ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        Cursor cursor = mDb.query(Views.RAW_CONTACTS,
370551bf5ea9531b9da72caff607dbdf35fd6f61cbe2Jeff Sharkey                new String[] { RawContacts._ID }, selection,
37064529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                selectionArgs, null, null, null);
37074529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        try {
37084529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            while (cursor.moveToNext()) {
37094529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                long rawContactId = cursor.getLong(0);
3710dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContact(rawContactId, values, callerIsSyncAdapter);
37114529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count++;
37124529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            }
37134529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        } finally {
37144529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            cursor.close();
37154529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
37164529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
37174529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return count;
37184529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    }
37194529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
3720dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContact(long rawContactId, ContentValues values,
3721dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
372224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
372324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // Enforce profile permissions if the raw contact is in the user's profile.
3724afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        enforceProfilePermissionForRawContact(mDb, rawContactId, true);
372524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
372696b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        final String selection = RawContacts._ID + " = ?";
372796b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(rawContactId);
372819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final boolean requestUndoDelete = (values.containsKey(RawContacts.DELETED)
372919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                && values.getAsInteger(RawContacts.DELETED) == 0);
373019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int previousDeleted = 0;
3731ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType = null;
3732ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName = null;
373319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        if (requestUndoDelete) {
373419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            Cursor cursor = mDb.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS, selection,
373596b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                    mSelectionArgs1, null, null, null);
373619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            try {
373719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                if (cursor.moveToFirst()) {
373819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    previousDeleted = cursor.getInt(RawContactsQuery.DELETED);
3739ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountType = cursor.getString(RawContactsQuery.ACCOUNT_TYPE);
3740ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountName = cursor.getString(RawContactsQuery.ACCOUNT_NAME);
374119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                }
374219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            } finally {
374319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                cursor.close();
374419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
374519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
374619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT);
374719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        }
3748f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
374996b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        int count = mDb.update(Tables.RAW_CONTACTS, values, selection, mSelectionArgs1);
37505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count != 0) {
3751f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            if (values.containsKey(RawContacts.AGGREGATION_MODE)) {
3752f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                int aggregationMode = values.getAsInteger(RawContacts.AGGREGATION_MODE);
3753f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov
3754f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // As per ContactsContract documentation, changing aggregation mode
3755f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // to DEFAULT should not trigger aggregation
3756f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                if (aggregationMode != RawContacts.AGGREGATION_MODE_DEFAULT) {
375769cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId, aggregationMode, false);
3758f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                }
3759f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            }
3760433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            if (values.containsKey(RawContacts.STARRED)) {
3761dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter) {
3762dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
3763dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            values.getAsLong(RawContacts.STARRED) != 0);
3764dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
37654529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                mContactAggregator.updateStarred(rawContactId);
3766dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
3767dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // if this raw contact is being associated with an account, then update the
3768dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // favorites group membership based on whether or not this contact is starred.
3769dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // If it is starred, add a group membership, if one doesn't already exist
3770dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // otherwise delete any matching group memberships.
3771dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
3772dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    boolean starred = 0 != DatabaseUtils.longForQuery(mDb,
3773dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            SELECTION_STARRED_FROM_RAW_CONTACTS,
3774dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            new String[]{Long.toString(rawContactId)});
3775dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId, starred);
3776dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
3777dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
3778dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3779dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // if this raw contact is being associated with an account, then add a
3780dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // group membership to the group marked as AutoAdd, if any.
3781dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
3782dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                addAutoAddMembership(rawContactId);
3783433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            }
3784dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3785285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (values.containsKey(RawContacts.SOURCE_ID)) {
37862b7a632bba423357ae5641f94da6a2f71afc523bDmitri Plotnikov                mContactAggregator.updateLookupKeyForRawContact(mDb, rawContactId);
3787285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
3788f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            if (values.containsKey(RawContacts.NAME_VERIFIED)) {
3789f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
3790f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // If setting NAME_VERIFIED for this raw contact, reset it for all
3791f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // other raw contacts in the same aggregate
3792f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                if (values.getAsInteger(RawContacts.NAME_VERIFIED) != 0) {
379378fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                    mDbHelper.resetNameVerifiedForOtherRawContacts(rawContactId);
3794f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                }
3795f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                mContactAggregator.updateDisplayNameForRawContact(mDb, rawContactId);
3796f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            }
379719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            if (requestUndoDelete && previousDeleted == 1) {
3798d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                mTransactionContext.rawContactInserted(rawContactId,
3799d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                        new Account(accountName, accountType));
380019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
38015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
38025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return count;
380333b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
380433b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
3805321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(Uri uri, ContentValues values, String selection,
3806f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
380720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
380820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
380920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
38105ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
381120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
381220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
381320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
381420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
381520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
3816b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
381720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
381820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
381997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
382097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
382197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    Data.IS_READ_ONLY + "=0");
382297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
382397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
3824653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        int count = 0;
382520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3826653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
3827653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // so we don't need to worry about updating data we don't have permission to read.
3828f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // This query will be allowed to return profiles, and we'll do the permission check
3829f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // within the loop.
38306ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro        Cursor c = queryLocal(uri.buildUpon()
3831f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                .appendQueryParameter(ContactsContract.ALLOW_PROFILE, "1").build(),
3832f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                DataRowHandler.DataUpdateQuery.COLUMNS,
38336ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                selection, selectionArgs, null, -1 /* directory ID */,
38346ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                true /* suppress profile check */);
3835653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        try {
3836653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            while(c.moveToNext()) {
383724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // Check profile permission for the raw contact that owns each data record.
383824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = c.getLong(DataRowHandler.DataUpdateQuery.RAW_CONTACT_ID);
3839afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForRawContact(mDb, rawContactId, true);
384024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
3841f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count += updateData(mValues, c, callerIsSyncAdapter);
384220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3843653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        } finally {
3844653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            c.close();
384520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
384620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3847653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return count;
384820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
384920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3850f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
3851653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        if (values.size() == 0) {
3852653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return 0;
3853321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
3854653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
3855f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        final String mimeType = c.getString(DataRowHandler.DataUpdateQuery.MIMETYPE);
3856a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
3857f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        boolean updated =
3858f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                rowHandler.update(mDb, mTransactionContext, values, c, callerIsSyncAdapter);
3859f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
3860f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
3861a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
3862f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        return updated ? 1 : 0;
3863321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    }
3864321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
38658c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(ContentValues values, String selection,
3866dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
38678c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        int count = 0;
3868ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        Cursor cursor = mDb.query(Views.CONTACTS,
386924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                new String[] { Contacts._ID, Contacts.IS_USER_PROFILE }, selection,
38708c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                selectionArgs, null, null, null);
38718c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        try {
38728c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            while (cursor.moveToNext()) {
38738c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                long contactId = cursor.getLong(0);
387424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
387524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // Check for profile write permission before updating a user's profile contact.
387624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                boolean isProfile = cursor.getInt(1) == 1;
387724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                if (isProfile) {
387824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    enforceProfilePermission(true);
387924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                }
388024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
3881dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateContactOptions(contactId, values, callerIsSyncAdapter);
38828c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count++;
38838c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            }
38848c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        } finally {
38858c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            cursor.close();
38868c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        }
38878c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
38888c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return count;
38898c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
38908c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
3891dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateContactOptions(long contactId, ContentValues values,
3892dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
3893d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
389424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // Check write permission if the contact is the user's profile.
3895afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        enforceProfilePermissionForContact(mDb, contactId, true);
389624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
38978c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
3898b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
3899d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
3900b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
3901d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
3902b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
3903d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
3904b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
3905d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
3906b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
3907d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.STARRED);
3908d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3909d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
39108c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.size() == 0) {
3911d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
3912d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
3913d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
39148c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.containsKey(RawContacts.STARRED)) {
3915c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            // Mark dirty when changing starred to trigger sync
39168c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            mValues.put(RawContacts.DIRTY, 1);
3917c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey        }
3918c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey
39194da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(contactId);
392097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        mDb.update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=?"
392197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                + " AND " + RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0", mSelectionArgs1);
39228c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
3923dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (mValues.containsKey(RawContacts.STARRED) && !callerIsSyncAdapter) {
3924ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann            Cursor cursor = mDb.query(Views.RAW_CONTACTS,
3925dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[] { RawContacts._ID }, RawContacts.CONTACT_ID + "=?",
3926dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    mSelectionArgs1, null, null, null);
3927dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            try {
3928dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                while (cursor.moveToNext()) {
3929dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    long rawContactId = cursor.getLong(0);
3930dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
3931dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            mValues.getAsLong(RawContacts.STARRED) != 0);
3932dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
3933dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } finally {
3934dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                cursor.close();
3935dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
3936dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
3937dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
39388c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // Copy changeable values to prevent automatically managed fields from
39398c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // being explicitly updated by clients.
39408c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
3941b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
39428c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
3943b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
39448c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
3945b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
39468c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
3947b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
39488c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
3949b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
39508c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.STARRED);
39518c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
39529b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        int rslt = mDb.update(Tables.CONTACTS, mValues, Contacts._ID + "=?", mSelectionArgs1);
39536e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori
39549b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        if (values.containsKey(Contacts.LAST_TIME_CONTACTED) &&
39559b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori                !values.containsKey(Contacts.TIMES_CONTACTED)) {
39569b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
39579b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
39589b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        }
39599b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        return rslt;
3960f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
3961d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3962127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
3963127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
39640c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId1 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID1);
39650c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId2 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID2);
396680c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
3967ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId1;
3968ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId2;
39690c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (rcId1 < rcId2) {
39700c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId1;
39710c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId2;
39720c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
39730c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId1;
39740c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId2;
3975b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
3976127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
39770c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
39784da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[0] = String.valueOf(rawContactId1);
39794da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[1] = String.valueOf(rawContactId2);
39800c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.delete(Tables.AGGREGATION_EXCEPTIONS,
39814da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    AggregationExceptions.RAW_CONTACT_ID1 + "=? AND "
39824da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    + AggregationExceptions.RAW_CONTACT_ID2 + "=?", mSelectionArgs2);
39830c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
39846bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            ContentValues exceptionValues = new ContentValues(3);
39856bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
39860c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
39870c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
39880c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
39890c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    exceptionValues);
3990127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
3991127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
39923389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
399369cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId1,
399469cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
399569cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId2,
399669cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
3997dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov
3998bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        mContactAggregator.aggregateContact(mTransactionContext, db, rawContactId1);
3999bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        mContactAggregator.aggregateContact(mTransactionContext, db, rawContactId2);
4000127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
4001127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
4002127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
4003127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
4004b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
4005b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
400670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    public void onAccountsUpdated(Account[] accounts) {
4007bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
40083826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
40093826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
4010bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected boolean updateAccountsInBackground(Account[] accounts) {
4011f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao        // TODO : Check the unit test.
4012e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov        boolean accountsChanged = false;
4013627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        HashSet<Account> existingAccounts = new HashSet<Account>();
401449d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov        mDb = mDbHelper.getWritableDatabase();
401570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        mDb.beginTransaction();
401670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        try {
4017dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            findValidAccounts(existingAccounts);
4018743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov
4019743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            // Add a row to the ACCOUNTS table for each new account
4020743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            for (Account account : accounts) {
4021743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                if (!existingAccounts.contains(account)) {
4022e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    accountsChanged = true;
4023743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                    mDb.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME
4024743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            + ", " + RawContacts.ACCOUNT_TYPE + ") VALUES (?, ?)",
4025743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            new String[] {account.name, account.type});
4026743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                }
4027743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            }
402848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
4029627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            // Remove all valid accounts from the existing account set. What is left
4030743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            // in the accountsToDelete set will be extra accounts whose data must be deleted.
4031627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            HashSet<Account> accountsToDelete = new HashSet<Account>(existingAccounts);
4032627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (Account account : accounts) {
4033627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                accountsToDelete.remove(account);
403470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
403570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
403633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            if (!accountsToDelete.isEmpty()) {
4037e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                accountsChanged = true;
4038e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                for (Account account : accountsToDelete) {
4039e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    Log.d(TAG, "removing data for removed account " + account);
4040e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    String[] params = new String[] {account.name, account.type};
4041e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4042e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.GROUPS +
4043e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Groups.ACCOUNT_NAME + " = ?" +
4044e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " AND " + Groups.ACCOUNT_TYPE + " = ?", params);
4045e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4046e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.PRESENCE +
4047e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (" +
4048e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    "SELECT " + RawContacts._ID +
4049e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " FROM " + Tables.RAW_CONTACTS +
4050e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
4051e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " AND " + RawContacts.ACCOUNT_TYPE + " = ?)", params);
4052e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4053e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.RAW_CONTACTS +
4054e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
4055e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + " = ?", params);
4056e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4057e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.SETTINGS +
4058e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Settings.ACCOUNT_NAME + " = ?" +
4059e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + Settings.ACCOUNT_TYPE + " = ?", params);
4060e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4061e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.ACCOUNTS +
4062e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + "=?" +
4063e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + "=?", params);
4064d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    mDb.execSQL(
4065d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            "DELETE FROM " + Tables.DIRECTORIES +
4066d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            " WHERE " + Directory.ACCOUNT_NAME + "=?" +
4067d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            " AND " + Directory.ACCOUNT_TYPE + "=?", params);
40684458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    resetDirectoryCache();
4069e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                }
4070e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov
407133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // Find all aggregated contacts that used to contain the raw contacts
407233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // we have just deleted and see if they are still referencing the deleted
4073e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                // names or photos.  If so, fix up those contacts.
407433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                HashSet<Long> orphanContactIds = Sets.newHashSet();
407533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                Cursor cursor = mDb.rawQuery("SELECT " + Contacts._ID +
407633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " FROM " + Tables.CONTACTS +
407733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " WHERE (" + Contacts.NAME_RAW_CONTACT_ID + " NOT NULL AND " +
407869cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                Contacts.NAME_RAW_CONTACT_ID + " NOT IN " +
407969cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + RawContacts._ID +
408069cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.RAW_CONTACTS + "))" +
408133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " OR (" + Contacts.PHOTO_ID + " NOT NULL AND " +
408233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                                Contacts.PHOTO_ID + " NOT IN " +
408369cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + Data._ID +
408469cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.DATA + "))", null);
408533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                try {
408633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    while (cursor.moveToNext()) {
408733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        orphanContactIds.add(cursor.getLong(0));
408833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    }
408933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                } finally {
409033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    cursor.close();
409133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
409233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
409333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                for (Long contactId : orphanContactIds) {
4094bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov                    mContactAggregator.updateAggregateData(mTransactionContext, contactId);
409533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
4096e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                mDbHelper.updateAllVisible();
4097bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov                updateSearchIndexInTransaction();
409833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            }
409933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
4100e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            if (accountsChanged) {
4101e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                mDbHelper.getSyncState().onAccountsChanged(mDb, accounts);
4102e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            }
410370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.setTransactionSuccessful();
410470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } finally {
410570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.endTransaction();
410670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
410773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.clear();
41083826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
41093826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (accountsChanged) {
41103826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateContactsAccountCount(accounts);
41113826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
41123826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
41133826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
4114afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov        return accountsChanged;
411570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
4116619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
41173826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateContactsAccountCount(Account[] accounts) {
41183826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        int count = 0;
41193826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        for (Account account : accounts) {
41203826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            if (isContactsAccount(account)) {
41213826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                count++;
41223826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            }
41233826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
41243826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mContactsAccountCount = count;
41253826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
41263826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
41273826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    protected boolean isContactsAccount(Account account) {
41283826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        final IContentService cs = ContentResolver.getContentService();
41293826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        try {
41303826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return cs.getIsSyncable(account, ContactsContract.AUTHORITY) > 0;
41313826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } catch (RemoteException e) {
41323826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            Log.e(TAG, "Cannot obtain sync flag for account: " + account, e);
41333826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return false;
41343826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
41353826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
41363826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
413772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void onPackageChanged(String packageName) {
4138bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_DIRECTORIES, packageName);
4139d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4140d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4141619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
4142627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     * Finds all distinct accounts present in the specified table.
4143627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     */
4144dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void findValidAccounts(Set<Account> validAccounts) {
4145743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov        Cursor c = mDb.rawQuery(
4146743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                "SELECT " + RawContacts.ACCOUNT_NAME + "," + RawContacts.ACCOUNT_TYPE +
4147743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                " FROM " + Tables.ACCOUNTS, null);
4148627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
4149627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            while (c.moveToNext()) {
4150dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!c.isNull(0) || !c.isNull(1)) {
4151627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    validAccounts.add(new Account(c.getString(0), c.getString(1)));
4152627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
4153627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
4154627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } finally {
4155627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            c.close();
4156627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
4157627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
4158627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
41594f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
41604f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
41614f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
416215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
416315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mReadAccessLatch);
416415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
4165d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String directory = getQueryParameter(uri, ContactsContract.DIRECTORY_PARAM_KEY);
4166385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directory == null) {
41673716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            return wrapCursor(uri,
41686ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder, -1, false));
4169385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directory.equals("0")) {
41703716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            return wrapCursor(uri,
41713716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder,
41726ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                            Directory.DEFAULT, false));
4173d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        } else if (directory.equals("1")) {
41743716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            return wrapCursor(uri,
41753716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder,
41766ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                            Directory.LOCAL_INVISIBLE, false));
4177d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4178d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4179d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        DirectoryInfo directoryInfo = getDirectoryAuthority(directory);
4180d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo == null) {
4181a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            Log.e(TAG, "Invalid directory ID: " + uri);
4182a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            return null;
4183d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4184d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4185d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Builder builder = new Uri.Builder();
4186d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.scheme(ContentResolver.SCHEME_CONTENT);
4187d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.authority(directoryInfo.authority);
4188d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.encodedPath(uri.getEncodedPath());
4189d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountName != null) {
4190d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_NAME, directoryInfo.accountName);
4191d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4192d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountType != null) {
4193d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, directoryInfo.accountType);
4194d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
41952e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
41962e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limit = getLimit(uri);
41972e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        if (limit != null) {
41982e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov            builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, limit);
41992e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        }
42002e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
4201d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Uri directoryUri = builder.build();
420209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
420309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        if (projection == null) {
420409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            projection = getDefaultProjection(uri);
420509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
420609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
4207332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        Cursor cursor = getContext().getContentResolver().query(directoryUri, projection, selection,
4208d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                selectionArgs, sortOrder);
42096ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
42106ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (cursor == null) {
42116ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return null;
42126ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
42136ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
4214547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        CrossProcessCursor crossProcessCursor = getCrossProcessCursor(cursor);
4215547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        if (crossProcessCursor != null) {
4216547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro            return wrapCursor(uri, cursor);
4217547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        } else {
4218547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro            return matrixCursorFromCursor(wrapCursor(uri, cursor));
4219547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        }
42203716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    }
42213716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
4222547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro    private Cursor wrapCursor(Uri uri, Cursor cursor) {
4223547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro
4224547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        // If the cursor doesn't contain a snippet column, don't bother wrapping it.
4225547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        if (cursor.getColumnIndex(SearchSnippetColumns.SNIPPET) < 0) {
4226547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro            return cursor;
4227547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        }
4228547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro
42293716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // Parse out snippet arguments for use when snippets are retrieved from the cursor.
42303716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String[] args = null;
42313716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String snippetArgs =
42323716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                getQueryParameter(uri, SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY);
42333716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        if (snippetArgs != null) {
42343716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            args = snippetArgs.split(",");
42353716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        }
42363716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
42373716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String query = uri.getLastPathSegment();
42383716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String startMatch = args != null && args.length > 0 ? args[0]
42393716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_START_MATCH;
42403716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String endMatch = args != null && args.length > 1 ? args[1]
42413716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_END_MATCH;
42423716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String ellipsis = args != null && args.length > 2 ? args[2]
42433716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_ELLIPSIS;
42443716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        int maxTokens = args != null && args.length > 3 ? Integer.parseInt(args[3])
42453716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_MAX_TOKENS;
42463716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
4247547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        return new SnippetizingCursorWrapper(cursor, query, startMatch, endMatch, ellipsis,
4248547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro                maxTokens);
42496ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
42506ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
42516ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    private CrossProcessCursor getCrossProcessCursor(Cursor cursor) {
42526ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        Cursor c = cursor;
42536ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (c instanceof CrossProcessCursor) {
42546ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return (CrossProcessCursor) c;
42556ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else if (c instanceof CursorWindow) {
42566ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return getCrossProcessCursor(((CursorWrapper) c).getWrappedCursor());
42576ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else {
42586ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return null;
42596ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
42606ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
42616ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
42626ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    public MatrixCursor matrixCursorFromCursor(Cursor cursor) {
42636ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        MatrixCursor newCursor = new MatrixCursor(cursor.getColumnNames());
42646ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        int numColumns = cursor.getColumnCount();
42656ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        String data[] = new String[numColumns];
42666ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        cursor.moveToPosition(-1);
42676ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        while (cursor.moveToNext()) {
42686ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            for (int i = 0; i < numColumns; i++) {
42696ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov                data[i] = cursor.getString(i);
42706ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            }
42716ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            newCursor.addRow(data);
4272332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        }
42736ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        return newCursor;
4274d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4275d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4276d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final class DirectoryQuery {
4277d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
4278d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory._ID,
4279d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.DIRECTORY_AUTHORITY,
4280d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_NAME,
4281d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_TYPE
4282d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        };
4283d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4284d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int DIRECTORY_ID = 0;
4285d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int AUTHORITY = 1;
4286d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_NAME = 2;
4287d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_TYPE = 3;
4288d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4289d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4290d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
4291d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Reads and caches directory information for the database.
4292d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
4293d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private DirectoryInfo getDirectoryAuthority(String directoryId) {
42944458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized (mDirectoryCache) {
42954458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            if (!mDirectoryCacheValid) {
42964458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCache.clear();
429749d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                SQLiteDatabase db = mDbHelper.getReadableDatabase();
429849d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                Cursor cursor = db.query(Tables.DIRECTORIES,
42994458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryQuery.COLUMNS,
43004458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        null, null, null, null, null);
43014458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                try {
43024458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    while (cursor.moveToNext()) {
43034458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryInfo info = new DirectoryInfo();
43044458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        String id = cursor.getString(DirectoryQuery.DIRECTORY_ID);
43054458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.authority = cursor.getString(DirectoryQuery.AUTHORITY);
43064458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
43074458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
43084458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        mDirectoryCache.put(id, info);
43094458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    }
43104458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                } finally {
43114458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    cursor.close();
4312d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                }
43134458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCacheValid = true;
4314d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4315d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
43164458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            return mDirectoryCache.get(directoryId);
43174458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
4318d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4319d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
432072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void resetDirectoryCache() {
43214458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized(mDirectoryCache) {
43224458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            mDirectoryCacheValid = false;
43234458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
432472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
432572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
43266ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro    private Cursor queryLocal(Uri uri, String[] projection, String selection,
43276ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro            String[] selectionArgs, String sortOrder, long directoryId,
43286ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro            boolean suppressProfileCheck) {
4329bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
4330bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            Log.v(TAG, "query: " + uri);
4331bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        }
43320b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov
4333b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
433435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4335d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
43361f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
4337c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limit = getLimit(uri);
4338c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4339a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
43404f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
434135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
4342b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().query(db, projection, selection,  selectionArgs,
434335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                        sortOrder);
434435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4345d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
4346763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
434724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                boolean existingWhere = appendLocalDirectorySelectionIfNeeded(qb, directoryId);
43486ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                appendProfileRestriction(qb, uri, Contacts.IS_USER_PROFILE, existingWhere,
43496ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                        suppressProfileCheck);
43506ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                sortOrder = prependProfileSortIfNeeded(uri, sortOrder, suppressProfileCheck);
4351619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
4352619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
4353619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
4354d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
43554a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
4356afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
4357763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
43584da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
43594da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
43606bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
43616bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
43626bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
43635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP:
43645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
43655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
43665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int segmentCount = pathSegments.size();
43675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount < 3) {
4368fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
4369fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
43705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
4371a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
43725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String lookupKey = pathSegments.get(2);
43735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount == 4) {
43745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
4375afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                    enforceProfilePermissionForContact(db, contactId, false);
43765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
4377763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
4378a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4379a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
4380a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4381a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
4382a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
43835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        return c;
43845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
43855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
43865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
4387763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
43884da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
43894da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
43904da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
43915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                break;
43925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
43935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
43942149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_DATA:
43952149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_DATA: {
43962149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
43972149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                int segmentCount = pathSegments.size();
43982149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount < 4) {
43992149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
44002149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                            "Missing a lookup key", uri));
44012149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
44022149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
44032149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount == 5) {
44042149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
4405afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                    enforceProfilePermissionForContact(db, contactId, false);
44062149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
44072149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    setTablesAndProjectionMapForData(lookupQb, uri, projection, false);
4408a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
4409a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
4410a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4411a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Data.CONTACT_ID, contactId, Data.LOOKUP_KEY, lookupKey);
4412a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
44132149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                        return c;
44142149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    }
44152149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
44162149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    // TODO see if the contact exists but has no data rows (rare)
44172149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
44182149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
44192149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
442024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long contactId = lookupContactIdByLookupKey(db, lookupKey);
4421afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
44222149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
442324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        String.valueOf(contactId));
44242149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                qb.appendWhere(" AND " + Data.CONTACT_ID + "=?");
44252149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                break;
44262149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            }
44272149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
44283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_ID_STREAM_ITEMS: {
44293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                long contactId = Long.parseLong(uri.getPathSegments().get(1));
4430afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
44313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
44323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
44333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(RawContactsColumns.CONCRETE_CONTACT_ID + "=?");
44343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
44353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
44363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
44373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_LOOKUP_STREAM_ITEMS:
44383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_LOOKUP_ID_STREAM_ITEMS: {
44393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                List<String> pathSegments = uri.getPathSegments();
44403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                int segmentCount = pathSegments.size();
44413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                if (segmentCount < 4) {
44423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
44433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                            "Missing a lookup key", uri));
44443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
44453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String lookupKey = pathSegments.get(2);
44463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                if (segmentCount == 5) {
44473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    long contactId = Long.parseLong(pathSegments.get(3));
4448afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                    enforceProfilePermissionForContact(db, contactId, false);
44493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
44503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    setTablesAndProjectionMapForStreamItems(lookupQb);
44513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
44523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
44533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                            RawContacts.CONTACT_ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
44543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    if (c != null) {
44553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        return c;
44563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    }
44573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
44583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
44593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
44603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                long contactId = lookupContactIdByLookupKey(db, lookupKey);
4461afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
44623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
44633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(RawContacts.CONTACT_ID + "=?");
44643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
44653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
44663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
4467f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
446842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
446924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long contactId = lookupContactIdByLookupKey(db, lookupKey);
4470afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
4471ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
4472f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                qb.setProjectionMap(sContactsVCardProjectionMap);
44734da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
447424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        String.valueOf(contactId));
44754da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
4476f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                break;
4477f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            }
4478f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
447942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
448042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
448142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                String currentDateString = dateFormat.format(new Date()).toString();
448242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                return db.rawQuery(
448342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    "SELECT" +
448442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " 'vcards_' || ? || '.vcf' AS " + OpenableColumns.DISPLAY_NAME + "," +
448542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " NULL AS " + OpenableColumns.SIZE,
448642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    new String[] { currentDateString });
448742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
448842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
4489ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_FILTER: {
4490916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                String filterParam = "";
4491ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
4492916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    filterParam = uri.getLastPathSegment();
4493ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
44947ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov                setTablesAndProjectionMapForContactsWithSnippet(
44957ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov                        qb, uri, projection, filterParam, directoryId);
44966ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                appendProfileRestriction(qb, uri, Contacts.IS_USER_PROFILE, false,
44976ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                        suppressProfileCheck);
44986ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                sortOrder = prependProfileSortIfNeeded(uri, sortOrder, suppressProfileCheck);
4499ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4500ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4501ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
4502ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT_FILTER:
4503ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT: {
45042f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Basically the resultant SQL should look like this:
45052f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // (SQL for listing starred items)
45062f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // UNION ALL
45072f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // (SQL for listing frequently contacted items)
45082f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // ORDER BY ...
45092f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
45102f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final boolean phoneOnly = readBooleanQueryParameter(
45112f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        uri, ContactsContract.STREQUENT_PHONE_ONLY, false);
45122f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (match == CONTACTS_STREQUENT_FILTER && uri.getPathSegments().size() > 3) {
45134a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
45144a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
4515e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
45165e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
45172f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    selection = DbQueryUtils.concatenateClauses(selection, sb.toString());
45184a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
45194a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
45202f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                String[] subProjection = null;
45215e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                if (projection != null) {
45222f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    subProjection = appendProjectionArg(projection, TIMES_USED_SORT_COLUMN);
45235e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                }
45245e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
45254a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                // Build the first query for starred
45264928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                setTablesAndProjectionMapForContacts(qb, uri, projection, false);
45274928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                qb.setProjectionMap(phoneOnly ?
45284928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                        sStrequentPhoneOnlyStarredProjectionMap
45294928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                        : sStrequentStarredProjectionMap);
45302f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                qb.appendWhere(DbQueryUtils.concatenateClauses(
45312f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        selection, Contacts.IS_USER_PROFILE + "=0"));
45322f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                qb.setStrict(true);
45332f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final String starredQuery = qb.buildQuery(subProjection,
453424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        Contacts.STARRED + "=1", Contacts._ID, null, null, null);
4535d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
45362f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Reset the builder.
4537d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
45382f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                qb.setStrict(true);
45394928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
45404928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                // Build the second query for frequent part.
45414928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                final String frequentQuery;
45424928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                if (phoneOnly) {
45434928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    final StringBuilder tableBuilder = new StringBuilder();
45444928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // In phone only mode, we need to look at view_data instead of
45454928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // contacts/raw_contacts to obtain actual phone numbers. One problem is that
45464928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // view_data is much larger than view_contacts, so our query might become much
45474928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // slower.
45484928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    //
45494928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // To avoid the possible slow down, we start from data usage table and join
45504928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // view_data to the table, assuming data usage table is quite smaller than
45514928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // data rows (almost always it should be), and we don't want any phone
45524928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // numbers not used by the user. This way sqlite is able to drop a number of
45534928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // rows in view_data in the early stage of data lookup.
45544928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    tableBuilder.append(Tables.DATA_USAGE_STAT
45554928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + " INNER JOIN " + Views.DATA + " " + Tables.DATA
45564928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "="
45574928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                                + DataColumns.CONCRETE_ID + " AND "
45584928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "="
45594928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                                + DataUsageStatColumns.USAGE_TYPE_INT_CALL + ")");
45604928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    appendContactPresenceJoin(tableBuilder, projection, RawContacts.CONTACT_ID);
45614928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    appendContactStatusUpdateJoin(tableBuilder, projection,
45624928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            ContactsColumns.LAST_STATUS_UPDATE_ID);
45634928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
45644928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setTables(tableBuilder.toString());
45654928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setProjectionMap(sStrequentPhoneOnlyFrequentProjectionMap);
45664928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.appendWhere(DbQueryUtils.concatenateClauses(
45674928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            selection,
45684928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            Contacts.STARRED + "=0 OR " + Contacts.STARRED + " IS NULL",
45694928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            MimetypesColumns.MIMETYPE + " IN ("
45704928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + "'" + Phone.CONTENT_ITEM_TYPE + "', "
45714928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + "'" + SipAddress.CONTENT_ITEM_TYPE + "')"));
45724928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    frequentQuery = qb.buildQuery(subProjection, null, null, null, null, null);
45734928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                } else {
45744928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    setTablesAndProjectionMapForContacts(qb, uri, projection, true);
45754928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setProjectionMap(sStrequentFrequentProjectionMap);
45764928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.appendWhere(DbQueryUtils.concatenateClauses(
45774928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            selection,
45784928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            "(" + Contacts.STARRED + " =0 OR " + Contacts.STARRED + " IS NULL)",
45794928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            Contacts.IS_USER_PROFILE + "=0"));
45804928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    frequentQuery = qb.buildQuery(subProjection,
45814928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            null, Contacts._ID, null, null, null);
45824928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                }
4583d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
4584d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
45852f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final String unionQuery =
45862f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
45872f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                                STREQUENT_ORDER_BY, STREQUENT_LIMIT);
45882f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
45892f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Here, we need to use selection / selectionArgs (supplied from users) "twice",
45902f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // as we want them both for starred items and for frequently contacted items.
45912f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                //
45922f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // e.g. if the user specify selection = "starred =?" and selectionArgs = "0",
45932f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // the resultant SQL should be like:
45942f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // SELECT ... WHERE starred =? AND ...
45952f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // UNION ALL
45962f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // SELECT ... WHERE starred =? AND ...
45972f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                String[] doubledSelectionArgs = null;
45982f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (selectionArgs != null) {
45992f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    final int length = selectionArgs.length;
46002f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    doubledSelectionArgs = new String[length * 2];
46017d7d0e95636344c01eb4e4d034791c199bee98e9Daisuke Miyakawa                    System.arraycopy(selectionArgs, 0, doubledSelectionArgs, 0, length);
46027d7d0e95636344c01eb4e4d034791c199bee98e9Daisuke Miyakawa                    System.arraycopy(selectionArgs, 0, doubledSelectionArgs, length, length);
46032f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                }
46042f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
46052f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                Cursor cursor = db.rawQuery(unionQuery, doubledSelectionArgs);
46062f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (cursor != null) {
46072f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    cursor.setNotificationUri(getContext().getContentResolver(),
4608d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
4609d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
46102f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                return cursor;
4611d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
4612d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
461345ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa            case CONTACTS_FREQUENT: {
461445ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                setTablesAndProjectionMapForContacts(qb, uri, projection, true);
461545ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                qb.setProjectionMap(sStrequentFrequentProjectionMap);
461645ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                qb.appendWhere(DbQueryUtils.concatenateClauses(
461745ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                        selection,
461845ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                        Contacts.IS_USER_PROFILE + "=0"));
461945ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                groupBy = Contacts._ID;
462045ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                if (!TextUtils.isEmpty(sortOrder)) {
462145ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                    sortOrder = FREQUENT_ORDER_BY + ", " + sortOrder;
462245ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                } else {
462345ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                    sortOrder = FREQUENT_ORDER_BY;
462445ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                }
462545ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                break;
462645ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa            }
462745ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa
4628ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_GROUP: {
4629763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
4630b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
463171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
46324a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
4633b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
4634b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
4635b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
4636b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
463724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
463824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
463924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForContacts(qb, uri, projection);
464024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(Contacts.IS_USER_PROFILE + "=1");
464124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
464224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
464324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
464424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_ENTITIES: {
464524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
464624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForEntities(qb, uri, projection);
464724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + Contacts.IS_USER_PROFILE + "=1");
464824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
464924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
465024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
465124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA: {
465224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
465324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
465424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1");
465524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
465624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
465724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
465824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA_ID: {
465924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
466024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
466124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
466224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + Data._ID + "=? AND "
466324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1");
466424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
466524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
466624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
466724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD: {
466824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
4669ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
467024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.setProjectionMap(sContactsVCardProjectionMap);
467124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(Contacts.IS_USER_PROFILE + "=1");
467224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
467324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
467424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
4675a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_DATA: {
46764a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
467782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
46784da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
46794da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
46806bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
46816bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
468200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
4683a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
46843653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
4685afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
468682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
46874da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
46884da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
46893653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
46903653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                break;
46913653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
46923653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
4693a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_ENTITIES: {
4694a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
4695a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
4696a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
4697a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
4698a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
4699a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
4700a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4701a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ENTITIES:
4702a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_ENTITIES: {
4703a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
4704a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                int segmentCount = pathSegments.size();
4705a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount < 4) {
4706a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
4707a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            "Missing a lookup key", uri));
4708a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
4709a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
4710a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount == 5) {
4711a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
4712a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
4713a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    setTablesAndProjectionMapForEntities(lookupQb, uri, projection);
4714a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
4715a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4716a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
4717a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4718a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.CONTACT_ID, contactId,
4719a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.LOOKUP_KEY, lookupKey);
4720a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
4721a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        return c;
4722a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    }
4723a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
4724a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4725a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
4726a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
4727a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
4728a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + Contacts.Entity.CONTACT_ID + "=?");
4729a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
4730a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
4731a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
47323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
47333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
47343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
47353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
47363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
47373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
47383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
47393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
47403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItemsColumns.CONCRETE_ID + "=?");
47413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
47423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
47433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
47443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_LIMIT: {
47456802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                MatrixCursor cursor = new MatrixCursor(new String[]{StreamItems.MAX_ITEMS}, 1);
47466802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                cursor.addRow(new Object[]{MAX_STREAM_ITEMS_PER_RAW_CONTACT});
47473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return cursor;
47483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
47493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
47503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
47513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
47523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
47533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
47543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
47553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
47563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
47573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
47583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemId);
47593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=?");
47603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
47613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
47623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
47633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
47643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
47653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
47663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
47673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemPhotoId);
47683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemId);
47693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=? AND " +
47703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=?");
47713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
47723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
47733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
4774f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case PHOTO_DIMENSIONS: {
4775f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                MatrixCursor cursor = new MatrixCursor(
4776f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{DisplayPhoto.DISPLAY_MAX_DIM, DisplayPhoto.THUMBNAIL_MAX_DIM},
4777f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        1);
4778f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                cursor.addRow(new Object[]{mMaxDisplayPhotoDim, mMaxThumbnailPhotoDim});
4779f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                return cursor;
4780f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
4781f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
47824a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case PHONES: {
478382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
478489c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
47852815f58f72f109790585931f601a63ddc02536a5Evan Millar                break;
47862815f58f72f109790585931f601a63ddc02536a5Evan Millar            }
47872815f58f72f109790585931f601a63ddc02536a5Evan Millar
478848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID: {
478982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
47904da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
479148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
47924da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
479348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
479448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
479548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
4796ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
479746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
479846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                Integer typeInt = sDataUsageTypeMap.get(typeParam);
479946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (typeInt == null) {
480046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    typeInt = DataUsageStatColumns.USAGE_TYPE_INT_CALL;
480146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
480246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
480389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
4804ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
48054a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
48064a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
4807a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    sb.append(" AND (");
48085e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
480945d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    boolean hasCondition = false;
48105e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    boolean orNeeded = false;
48115e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String normalizedName = NameNormalizer.normalize(filterParam);
48125e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (normalizedName.length() > 0) {
4813155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN " +
4814155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
4815155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.SEARCH_INDEX +
4816155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " JOIN " + Tables.RAW_CONTACTS +
4817155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
4818155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
4819155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE " + SearchIndexColumns.NAME + " MATCH ");
48202352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                        DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filterParam) + "*");
4821155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(")");
48225e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        orNeeded = true;
482345d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
48245e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
48255e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
4826892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    String number = PhoneNumberUtils.normalizeNumber(filterParam);
4827892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (!TextUtils.isEmpty(number)) {
48285e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        if (orNeeded) {
48295e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                            sb.append(" OR ");
48305e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        }
48315e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data._ID +
4832892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                " IN (SELECT DISTINCT " + PhoneLookupColumns.DATA_ID
4833892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " FROM " + Tables.PHONE_LOOKUP
4834892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
4835892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append(number);
4836892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append("%')");
483745d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
483845d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    }
483945d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov
484045d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    if (!hasCondition) {
484145d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // If it is neither a phone number nor a name, the query should return
484245d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // an empty cursor.  Let's ensure that.
484345d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        sb.append("0");
48445e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
48455e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
4846a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
4847ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
48485e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = PhoneColumns.NORMALIZED_NUMBER + "," + RawContacts.CONTACT_ID;
4849a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
485046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
485146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
485246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = accountPromotionSortOrder + ", " + PHONE_FILTER_SORT_ORDER;
485346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    } else {
485446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = PHONE_FILTER_SORT_ORDER;
485546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    }
4856a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
4857ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4858ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4859ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
48604a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case EMAILS: {
486182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
486289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
48634a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                break;
48644a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
48654a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
486648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID: {
486782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
48684da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
48694da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'"
48704da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        + " AND " + Data._ID + "=?");
487148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
487248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
487348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
48745e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_LOOKUP: {
487582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
487689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
48774a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
487808768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String email = uri.getLastPathSegment();
487908768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String address = mDbHelper.extractAddressFromEmailAddress(email);
488008768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, address);
488108768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    qb.appendWhere(" AND UPPER(" + Email.DATA + ")=UPPER(?)");
48824a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
4883ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4884ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4885ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
48865e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_FILTER: {
488746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
488846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                Integer typeInt = sDataUsageTypeMap.get(typeParam);
488946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (typeInt == null) {
489046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    typeInt = DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT;
489146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
489246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
489307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                String filterParam = null;
48947d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa
489507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
489607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    filterParam = uri.getLastPathSegment();
489707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    if (TextUtils.isEmpty(filterParam)) {
489807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                        filterParam = null;
489907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    }
490007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                }
49015e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
490207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (filterParam == null) {
490307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    // If the filter is unspecified, return nothing
490407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    qb.appendWhere(" AND 0");
490507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                } else {
490607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    StringBuilder sb = new StringBuilder();
490707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(" AND " + Data._ID + " IN (");
490807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(
490907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            "SELECT " + Data._ID +
491007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " FROM " + Tables.DATA +
49112a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                            " WHERE " + DataColumns.MIMETYPE_ID + "=");
49122a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                    sb.append(mDbHelper.getMimeTypeIdForEmail());
49132a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                    sb.append(" AND " + Data.DATA1 + " LIKE ");
491407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(sb, filterParam + '%');
491520938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                    if (!filterParam.contains("@")) {
4916155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(
4917155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " UNION SELECT " + Data._ID +
4918155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.DATA +
4919155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE +" + DataColumns.MIMETYPE_ID + "=");
4920155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(mDbHelper.getMimeTypeIdForEmail());
4921155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(" AND " + Data.RAW_CONTACT_ID + " IN " +
4922155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
4923155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.SEARCH_INDEX +
4924155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " JOIN " + Tables.RAW_CONTACTS +
4925155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
4926155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
4927155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE " + SearchIndexColumns.NAME + " MATCH ");
49282352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                        DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filterParam) + "*");
4929155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(")");
49305e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
49315e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
4932a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
49335e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                }
49345e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
4935a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
493646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
493746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
493846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = accountPromotionSortOrder + ", " + EMAIL_FILTER_SORT_ORDER;
49397d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                    } else {
49407d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                        sortOrder = EMAIL_FILTER_SORT_ORDER;
49417d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                    }
4942a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
49435e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                break;
49445e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
49455e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
4946ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
494782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
494889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
494989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
4950ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4951ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4952ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
495348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
495482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
49554da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
495648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
495748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
49584da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
495948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
496048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
496148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
49625ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
4963763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
49646ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                appendProfileRestriction(qb, uri, RawContacts.RAW_CONTACT_IS_USER_PROFILE, true,
49656ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                        suppressProfileCheck);
49664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
49674f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
49684f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
49695ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
49705ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
4971afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForRawContact(db, rawContactId, false);
4972763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
49734da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
49744da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
49754f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
49764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
49774f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
49785ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
49795ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
498082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
49814da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
49824da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=?");
49836ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                appendProfileRestriction(qb, uri, RawContacts.RAW_CONTACT_IS_USER_PROFILE, true,
49846ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                        suppressProfileCheck);
498524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
498624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
498724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
49883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case RAW_CONTACTS_ID_STREAM_ITEMS: {
49893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
4990afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForRawContact(db, rawContactId, false);
49913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
49923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
49933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItems.RAW_CONTACT_ID + "=?");
49943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
49953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
499624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
499724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS: {
499824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
499924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForRawContacts(qb, uri);
500024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1");
500124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
500224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
500324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
500424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID: {
500524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
500624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = ContentUris.parseId(uri);
500724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
500824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForRawContacts(qb, uri);
500924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1 AND "
501024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        + RawContacts._ID + "=?");
501124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
501224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
501324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
501424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID_DATA: {
501524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
501624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(2));
501724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
501824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
501924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1 AND "
502024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        + Data.RAW_CONTACT_ID + "=?");
502124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
502224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
502324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
502424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID_ENTITIES: {
502524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
502624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(2));
502724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
502824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForRawEntities(qb, uri);
502924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1 AND "
503024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        + RawContacts._ID + "=?");
5031e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
5032e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
5033e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
5034e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            case DATA: {
503582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
50366ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                appendProfileRestriction(qb, uri, RawContacts.RAW_CONTACT_IS_USER_PROFILE, true,
50376ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                        suppressProfileCheck);
5038e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
5039e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
5040e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
50414f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case DATA_ID: {
504224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long dataId = ContentUris.parseId(uri);
5043afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForData(db, dataId, false);
504482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
50454da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
50464da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
5047a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                break;
5048a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            }
5049a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov
5050a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
50514a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
5052a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
5053a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
5054a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
5055892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    sortOrder = " length(lookup.normalized_number) DESC";
5056a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
5057a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
5058e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
5059e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                String numberE164 = PhoneNumberUtils.formatNumberToE164(number,
5060e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                        mDbHelper.getCurrentCountryIso());
5061892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                String normalizedNumber =
5062892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        PhoneNumberUtils.normalizeNumber(number);
5063892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                mDbHelper.buildPhoneLookupAndContactQuery(qb, normalizedNumber, numberE164);
5064e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                qb.setProjectionMap(sPhoneLookupProjectionMap);
5065e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                // Phone lookup cannot be combined with a selection
5066e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selection = null;
5067e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selectionArgs = null;
5068a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
5069a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
5070a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
5071ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
5072ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.GROUPS);
5073ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
507489c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
5075ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5076ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5077ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5078ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
5079ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.GROUPS);
5080ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
50814da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
50824da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Groups._ID + "=?");
5083ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5084ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5085ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5086ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
5087ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.GROUPS + " AS groups");
5088ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsSummaryProjectionMap);
508989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
509089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                groupBy = Groups._ID;
5091ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5092ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5093ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5094b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
50950c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
5096b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
5097b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
5098b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
5099b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
510031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
5101d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
51022d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                String filter = null;
51032d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
51042d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                    filter = uri.getPathSegments().get(3);
51052d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                }
510631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
5107d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                if (limit != null) {
5108d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(limit);
510931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
511031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
511131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
511231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
51135b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                ArrayList<AggregationSuggestionParameter> parameters = null;
51145b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                List<String> query = uri.getQueryParameters("query");
51155b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                if (query != null && !query.isEmpty()) {
51165b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    parameters = new ArrayList<AggregationSuggestionParameter>(query.size());
51175b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    for (String parameter : query) {
51185b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        int offset = parameter.indexOf(':');
51195b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        parameters.add(offset == -1
51205b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                ? new AggregationSuggestionParameter(
512176dfa406e2cde19c824983c37fc92c1c5bf63eecDaniel Lehmann                                        AggregationSuggestions.PARAMETER_MATCH_NAME,
51225b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter)
51235b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                : new AggregationSuggestionParameter(
51245b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(0, offset),
51255b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(offset + 1)));
51265b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    }
51275b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                }
51285b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
5129763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
51307581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov
51317581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov                return mContactAggregator.queryAggregationSuggestions(qb, projection, contactId,
51325b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        maxSuggestions, filter, parameters);
513331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
513431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
5135eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
5136eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setTables(Tables.SETTINGS);
5137eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setProjectionMap(sSettingsProjectionMap);
513889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
5139e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
5140e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // When requesting specific columns, this query requires
5141e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // late-binding of the GroupMembership MIME-type.
5142b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                final String groupMembershipMimetypeId = Long.toString(mDbHelper
5143e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
514482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
5145b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_COUNT)) {
5146e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
5147e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
514882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
5149b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_WITH_PHONES)) {
5150e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
5151e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
5152e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
5153eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
5154eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
5155eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
515682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
51570a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
51585ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
51595ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
51605ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
516182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES_ID: {
51620a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
51634da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
51644da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(DataColumns.CONCRETE_ID + "=?");
51655ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
51665ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
51675ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
5168c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS: {
5169174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(
5170174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        db, uri, projection, limit);
5171c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5172c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
5173c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT: {
51742d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                String lookupKey = uri.getLastPathSegment();
5175174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                String filter = getQueryParameter(
5176174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        uri, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
5177174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchShortcutRefresh(
5178174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        db, projection, lookupKey, filter);
5179c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5180c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
51811b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS:
5182ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
51831b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
51841b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
51851b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
51861b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_WITH_PHONES:
5187ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
51881b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
51891b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.HAS_PHONE_NUMBER + "=1");
51901b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
51911b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
51921b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_FAVORITES:
5193ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
51941b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
51951b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.STARRED + "=1");
51961b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
51971b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
51981b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_GROUP_NAME:
5199ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
52001b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
520171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
52021b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
52031b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
52041b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
520546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITIES: {
5206a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
520746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
520846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
520946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
521046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITY_ID: {
521146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
5212a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
52134da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
52144da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
521546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
521646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
521746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
521809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            case PROVIDER_STATUS: {
521909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                return queryProviderStatus(uri, projection);
522009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
522109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5222d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES : {
5223d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
5224d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
5225d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
5226d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
5227d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
5228d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID : {
5229385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                long id = ContentUris.parseId(uri);
5230d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
5231d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
5232385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(id));
5233d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.appendWhere(Directory._ID + "=?");
5234d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
5235d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
5236d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
52377a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            case COMPLETE_NAME: {
52387a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                return completeName(uri, projection);
52397a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
52407a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
52414f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
5242f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
5243c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                        sortOrder, limit);
52444f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
52454f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
524609e69522745551522c55dff27424496f255def46Daniel Lehmann        qb.setStrict(true);
52477f786e5cbde9975b9632beb9b6d19eeef8a64cf1Dmitri Plotnikov
5248ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        Cursor cursor =
5249ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit);
5250ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (readBooleanQueryParameter(uri, ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, false)) {
5251ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            cursor = bundleLetterCountExtras(cursor, db, qb, selection, selectionArgs, sortOrder);
5252ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5253ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        return cursor;
52545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
52555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
52565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
52575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String selection, String[] selectionArgs, String sortOrder, String groupBy,
52585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String limit) {
5259038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        if (projection != null && projection.length == 1
5260038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                && BaseColumns._COUNT.equals(projection[0])) {
5261038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            qb.setProjectionMap(sCountProjectionMap);
5262038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
52635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
52645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sortOrder, limit);
52654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
52664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
52674f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
52684f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
52694f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
52704f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
527109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    /**
527209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     * Creates a single-row cursor containing the current status of the provider.
527309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     */
527409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private Cursor queryProviderStatus(Uri uri, String[] projection) {
527509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
527609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        RowBuilder row = cursor.newRow();
527709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
527809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            if (ProviderStatus.STATUS.equals(projection[i])) {
527909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mProviderStatus);
528009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            } else if (ProviderStatus.DATA1.equals(projection[i])) {
528109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mEstimatedStorageRequirement);
528209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
528309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        }
528409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        return cursor;
528509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    }
528609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5287a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /**
5288a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * Runs the query with the supplied contact ID and lookup ID.  If the query succeeds,
5289a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * it returns the resulting cursor, otherwise it returns null and the calling
5290a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * method needs to resolve the lookup key and rerun the query.
5291a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     */
5292a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private Cursor queryWithContactIdAndLookupKey(SQLiteQueryBuilder lookupQb,
5293a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            SQLiteDatabase db, Uri uri,
5294a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection, String selection, String[] selectionArgs,
5295a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String sortOrder, String groupBy, String limit,
5296a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn, long contactId, String lookupKeyColumn, String lookupKey) {
5297a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        String[] args;
5298a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (selectionArgs == null) {
5299a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[2];
5300a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        } else {
5301a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[selectionArgs.length + 2];
5302a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
5303a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5304a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[0] = String.valueOf(contactId);
5305a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[1] = Uri.encode(lookupKey);
5306a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        lookupQb.appendWhere(contactIdColumn + "=? AND " + lookupKeyColumn + "=?");
5307a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        Cursor c = query(db, lookupQb, projection, selection, args, sortOrder,
5308a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                groupBy, limit);
5309a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (c.getCount() != 0) {
5310a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return c;
5311a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5312a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5313a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        c.close();
5314a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return null;
5315a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
531609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5317bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov    private static final class AddressBookIndexQuery {
5318bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String LETTER = "letter";
5319bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String TITLE = "title";
5320bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String COUNT = "count";
5321ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5322bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
5323bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                LETTER, TITLE, COUNT
5324ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        };
5325ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5326bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_LETTER = 0;
5327bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_TITLE = 1;
5328bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_COUNT = 2;
5329bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
533024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // The first letter of the sort key column is what is used for the index headings, except
533124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // in the case of the user's profile, in which case it is empty.
533224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        public static final String SECTION_HEADING_TEMPLATE =
533324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                "(CASE WHEN %1$s=1 THEN '' ELSE SUBSTR(%2$s,1,1) END)";
533424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
5335de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa        public static final String ORDER_BY = LETTER + " COLLATE " + PHONEBOOK_COLLATOR_NAME;
5336ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
5337ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5338ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    /**
5339ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * Computes counts by the address book index titles and adds the resulting tally
5340ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * to the returned cursor as a bundle of extras.
5341ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     */
5342ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    private Cursor bundleLetterCountExtras(Cursor cursor, final SQLiteDatabase db,
5343ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            SQLiteQueryBuilder qb, String selection, String[] selectionArgs, String sortOrder) {
5344ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortKey;
5345ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5346ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // The sort order suffix could be something like "DESC".
5347ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // We want to preserve it in the query even though we will change
5348ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // the sort column itself.
5349ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortOrderSuffix = "";
5350ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (sortOrder != null) {
535124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
535224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // If the sort order contains one of the "is_profile" columns, we need to strip it out
535324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // first.
535424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            if (sortOrder.contains(Contacts.IS_USER_PROFILE)
535524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    || sortOrder.contains(RawContacts.RAW_CONTACT_IS_USER_PROFILE)) {
535624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                String[] splitOrderClauses = sortOrder.split(",");
535724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                StringBuilder rejoinedClause = new StringBuilder();
535824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                for (String orderClause : splitOrderClauses) {
535924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    if (!orderClause.contains(Contacts.IS_USER_PROFILE)
536024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                            && !orderClause.contains(RawContacts.RAW_CONTACT_IS_USER_PROFILE)) {
536124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        if (rejoinedClause.length() > 0) {
536224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                            rejoinedClause.append(", ");
536324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        }
536424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        rejoinedClause.append(orderClause.trim());
536524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    }
536624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                }
536724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                sortOrder = rejoinedClause.toString();
536824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
536924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
5370ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int spaceIndex = sortOrder.indexOf(' ');
5371ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            if (spaceIndex != -1) {
5372ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder.substring(0, spaceIndex);
5373ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortOrderSuffix = sortOrder.substring(spaceIndex);
5374ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            } else {
5375ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder;
5376ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
5377ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } else {
5378ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            sortKey = Contacts.SORT_KEY_PRIMARY;
5379ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5380ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5381bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        String locale = getLocale().toString();
5382ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        HashMap<String, String> projectionMap = Maps.newHashMap();
538324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
538424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // The user profile column varies depending on the view.
5385ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        String profileColumn = qb.getTables().contains(Views.CONTACTS)
538624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                ? Contacts.IS_USER_PROFILE
538724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                : RawContacts.RAW_CONTACT_IS_USER_PROFILE;
538824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        String sectionHeading = String.format(
538924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                AddressBookIndexQuery.SECTION_HEADING_TEMPLATE, profileColumn, sortKey);
5390bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.LETTER,
539124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                sectionHeading + " AS " + AddressBookIndexQuery.LETTER);
5392bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5393bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        /**
5394bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * Use the GET_PHONEBOOK_INDEX function, which is an android extension for SQLite3,
5395bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * to map the first letter of the sort key to a character that is traditionally
5396bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * used in phonebooks to represent that letter.  For example, in Korean it will
5397bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * be the first consonant in the letter; for Japanese it will be Hiragana rather
5398bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * than Katakana.
5399bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         */
5400ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.TITLE,
540124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                "GET_PHONEBOOK_INDEX(" + sectionHeading + ",'" + locale + "')"
5402bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                        + " AS " + AddressBookIndexQuery.TITLE);
5403ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.COUNT,
5404ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                "COUNT(" + Contacts._ID + ") AS " + AddressBookIndexQuery.COUNT);
5405ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        qb.setProjectionMap(projectionMap);
5406ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5407f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov        Cursor indexCursor = qb.query(db, AddressBookIndexQuery.COLUMNS, selection, selectionArgs,
5408ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY, null /* having */,
5409ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY + sortOrderSuffix);
5410ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5411ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        try {
5412f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            int groupCount = indexCursor.getCount();
5413ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            String titles[] = new String[groupCount];
5414ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int counts[] = new int[groupCount];
5415bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            int indexCount = 0;
5416bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            String currentTitle = null;
5417bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5418bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // Since GET_PHONEBOOK_INDEX is a many-to-1 function, we may end up
5419bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // with multiple entries for the same title.  The following code
5420bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // collapses those duplicates.
5421ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            for (int i = 0; i < groupCount; i++) {
5422f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov                indexCursor.moveToNext();
5423bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String title = indexCursor.getString(AddressBookIndexQuery.COLUMN_TITLE);
5424bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int count = indexCursor.getInt(AddressBookIndexQuery.COLUMN_COUNT);
5425bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                if (indexCount == 0 || !TextUtils.equals(title, currentTitle)) {
5426bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    titles[indexCount] = currentTitle = title;
5427bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount] = count;
5428bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    indexCount++;
5429bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                } else {
5430bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount - 1] += count;
5431bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                }
5432bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            }
5433bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5434bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            if (indexCount < groupCount) {
5435bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String[] newTitles = new String[indexCount];
5436bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(titles, 0, newTitles, 0, indexCount);
5437bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                titles = newTitles;
5438bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5439bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int[] newCounts = new int[indexCount];
5440bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(counts, 0, newCounts, 0, indexCount);
5441bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                counts = newCounts;
5442ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
5443ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5444e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return new AddressBookCursor((CrossProcessCursor) cursor, titles, counts);
5445ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } finally {
5446f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            indexCursor.close();
5447ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5448ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
5449ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
54502d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    /**
545192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Returns the contact Id for the contact identified by the lookupKey.
545292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Robust against changes in the lookup key: if the key has changed, will
545392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * look up the contact by the raw contact IDs or name encoded in the lookup
545492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * key.
54552d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     */
54562d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    public long lookupContactIdByLookupKey(SQLiteDatabase db, String lookupKey) {
54575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactLookupKey key = new ContactLookupKey();
54585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ArrayList<LookupKeySegment> segments = key.parse(lookupKey);
54595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
546092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        long contactId = -1;
546192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_SOURCE_ID)) {
546292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdBySourceIds(db, segments);
546392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
546492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
546592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
546692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
546792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
546892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        boolean hasRawContactIds =
546992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID);
547092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds) {
547192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdByRawContactIds(db, segments);
547292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
547392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
547492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
547592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
547692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
547792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds
547892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                || lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME)) {
54795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            contactId = lookupContactIdByDisplayNames(db, segments);
54805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
54815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
54825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return contactId;
54835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
54845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
54855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupBySourceIdQuery {
54865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
54875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
54885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
54895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
54905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
54915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
54925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.SOURCE_ID
54935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
54945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
54955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
54965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
54975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
54985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int SOURCE_ID = 3;
54995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
55005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
55015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdBySourceIds(SQLiteDatabase db,
55025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
55035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
55045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(RawContacts.SOURCE_ID + " IN (");
55055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
55065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
550792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID) {
55085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
55095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
55105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
55115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
55125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
55135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
55145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
55155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupBySourceIdQuery.TABLE, LookupBySourceIdQuery.COLUMNS,
55165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
55175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
55185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
55195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE);
55205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME);
55215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
55225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
55235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID);
55245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
55255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
552692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID
552792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
55285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(sourceId)) {
55295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupBySourceIdQuery.CONTACT_ID);
55305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
55315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
55325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
55335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
55345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
55355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
55365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
55375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
55385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
55395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
55405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
554192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByRawContactIdQuery {
554292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
55435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
55445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
55455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
55465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
55475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
554892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts._ID,
55495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
55505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
55515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
55525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
55535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
555492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ID = 3;
55555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
55565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
555792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByRawContactIds(SQLiteDatabase db,
555892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            ArrayList<LookupKeySegment> segments) {
555992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
556092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(RawContacts._ID + " IN (");
55615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
55625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
556392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
556492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(segment.rawContactId);
556592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(",");
55665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
55675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
556892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
556992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
55705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
557192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        Cursor c = db.query(LookupByRawContactIdQuery.TABLE, LookupByRawContactIdQuery.COLUMNS,
557292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                 sb.toString(), null, null, null, null);
557392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        try {
557492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            while (c.moveToNext()) {
557592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountType = c.getString(LookupByRawContactIdQuery.ACCOUNT_TYPE);
557692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountName = c.getString(LookupByRawContactIdQuery.ACCOUNT_NAME);
557792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                int accountHashCode =
557892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
557992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String rawContactId = c.getString(LookupByRawContactIdQuery.ID);
558092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
558192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
558292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID
558392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
558492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && segment.rawContactId.equals(rawContactId)) {
558592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        segment.contactId = c.getLong(LookupByRawContactIdQuery.CONTACT_ID);
558692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        break;
558792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    }
558892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                }
558992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
559092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        } finally {
559192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            c.close();
55925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
55935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
559492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return getMostReferencedContactId(segments);
559592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
559692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
559792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByDisplayNameQuery {
559892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
559992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
560092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String COLUMNS[] = {
560192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.CONTACT_ID,
560292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
560392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
560492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME
560592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        };
560692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
560792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int CONTACT_ID = 0;
560892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_TYPE = 1;
560992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_NAME = 2;
561092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int NORMALIZED_NAME = 3;
561192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
561292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
561392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByDisplayNames(SQLiteDatabase db,
561492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
56155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
56165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(NameLookupColumns.NORMALIZED_NAME + " IN (");
56175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
56185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
561992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
562092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
56215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
56225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
56235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
56245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
56255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
56265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
56275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                + " AND " + RawContacts.CONTACT_ID + " NOT NULL");
56285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
56295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupByDisplayNameQuery.TABLE, LookupByDisplayNameQuery.COLUMNS,
56305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
56315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
56325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
56335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE);
56345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME);
56355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
56365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
56375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME);
56385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
56395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
564092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if ((segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
564192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID)
564292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
56435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(name)) {
56445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupByDisplayNameQuery.CONTACT_ID);
56455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
56465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
56475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
56485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
56495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
56505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
56515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
56525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
56535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
56545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
56555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
565692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private boolean lookupKeyContainsType(ArrayList<LookupKeySegment> segments, int lookupType) {
565792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
565892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            LookupKeySegment segment = segments.get(i);
565992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == lookupType) {
566092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return true;
566192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
566292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
566392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
566492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return false;
566592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
566692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
5667ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateLookupKeyForRawContact(SQLiteDatabase db, long rawContactId) {
5668ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov        mContactAggregator.updateLookupKeyForRawContact(db, rawContactId);
5669ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    }
5670ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov
56715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /**
56725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     * Returns the contact ID that is mentioned the highest number of times.
56735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     */
56745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long getMostReferencedContactId(ArrayList<LookupKeySegment> segments) {
56755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Collections.sort(segments);
56765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
56775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long bestContactId = -1;
56785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int bestRefCount = 0;
56795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
56805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = -1;
56815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int count = 0;
56825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
56835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int segmentCount = segments.size();
56845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segmentCount; i++) {
56855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
56865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.contactId != -1) {
56875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segment.contactId == contactId) {
56885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count++;
56895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                } else {
56905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (count > bestRefCount) {
56915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestContactId = contactId;
56925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestRefCount = count;
56935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
56945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    contactId = segment.contactId;
56955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count = 1;
56965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
56975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
56985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
56995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count > bestRefCount) {
57005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return contactId;
57015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
57025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return bestContactId;
57035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
57045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
57055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
5706763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
5707763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            String[] projection) {
57084928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa        setTablesAndProjectionMapForContacts(qb, uri, projection, false);
57092f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    }
57102f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
57112f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    /**
57124928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * @param includeDataUsageStat true when the table should include DataUsageStat table.
57134928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * Note that this uses INNER JOIN instead of LEFT OUTER JOIN, so some of data in Contacts
57144928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * may be dropped.
57152f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     */
57162f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
57174928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            String[] projection, boolean includeDataUsageStat) {
571882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5719ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.CONTACTS);
57202f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
57212f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa        // Just for frequently contacted contacts in Strequent Uri handling.
57224928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa        if (includeDataUsageStat) {
57232f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            sb.append(" INNER JOIN " +
5724ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                    Views.DATA_USAGE_STAT + " AS " + Tables.DATA_USAGE_STAT +
57252f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    " ON (" +
57262f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    DbQueryUtils.concatenateClauses(
57272f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                            DataUsageStatColumns.CONCRETE_TIMES_USED + " > 0",
57284928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            RawContacts.CONTACT_ID + "=" + Views.CONTACTS + "." + Contacts._ID) +
57292f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    ")");
57302f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa        }
57312f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
57327ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
57337ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
5734916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setTables(sb.toString());
5735916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionMap);
5736916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    }
5737916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5738916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /**
5739916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * Finds name lookup records matching the supplied filter, picks one arbitrary match per
5740916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * contact and joins that with other contacts tables.
5741916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     */
5742916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private void setTablesAndProjectionMapForContactsWithSnippet(SQLiteQueryBuilder qb, Uri uri,
57437ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov            String[] projection, String filter, long directoryId) {
57447ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov
57457ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
5746ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.CONTACTS);
5747916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
574803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        if (filter != null) {
574903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            filter = filter.trim();
575003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        }
575103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
575230cc766756461da8d53933f88ea01dd2272a90ebDmitri Plotnikov        if (TextUtils.isEmpty(filter) || (directoryId != -1 && directoryId != Directory.DEFAULT)) {
575330cc766756461da8d53933f88ea01dd2272a90ebDmitri Plotnikov            sb.append(" JOIN (SELECT NULL AS " + SearchSnippetColumns.SNIPPET + " WHERE 0)");
57545e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        } else {
57555e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            appendSearchIndexJoin(sb, uri, projection, filter);
57565e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        }
57577ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
57587ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
575903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        qb.setTables(sb.toString());
576003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionWithSnippetMap);
576103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    }
5762916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
576303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private void appendSearchIndexJoin(
576403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            StringBuilder sb, Uri uri, String[] projection, String filter) {
5765916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5766174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET)) {
576703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String[] args = null;
576803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String snippetArgs =
576903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                    getQueryParameter(uri, SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY);
577003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            if (snippetArgs != null) {
577103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                args = snippetArgs.split(",");
577203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
577303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
57745e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String startMatch = args != null && args.length > 0 ? args[0]
57755e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_START_MATCH;
57765e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String endMatch = args != null && args.length > 1 ? args[1]
57775e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_END_MATCH;
57785e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String ellipsis = args != null && args.length > 2 ? args[2]
57795e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_ELLIPSIS;
57805e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            int maxTokens = args != null && args.length > 3 ? Integer.parseInt(args[3])
57815e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_MAX_TOKENS;
57825e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
5783174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            appendSearchIndexJoin(
5784174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                    sb, filter, true, startMatch, endMatch, ellipsis, maxTokens);
5785174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        } else {
5786174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            appendSearchIndexJoin(sb, filter, false, null, null, null, 0);
5787174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
5788174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    }
5789174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
5790174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    public void appendSearchIndexJoin(StringBuilder sb, String filter,
5791174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            boolean snippetNeeded, String startMatch, String endMatch, String ellipsis,
5792174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            int maxTokens) {
5793174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        boolean isEmailAddress = false;
5794174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String emailAddress = null;
5795174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        boolean isPhoneNumber = false;
5796174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String phoneNumber = null;
5797174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String numberE164 = null;
5798174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
57993716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // If the query consists of a single word, we can do snippetizing after-the-fact for a
58003716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // performance boost.
58013716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        boolean singleTokenSearch = filter.split(QUERY_TOKENIZER_REGEX).length == 1;
58023716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
5803174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (filter.indexOf('@') != -1) {
5804174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            emailAddress = mDbHelper.extractAddressFromEmailAddress(filter);
5805174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            isEmailAddress = !TextUtils.isEmpty(emailAddress);
5806174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        } else {
5807174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            isPhoneNumber = isPhoneNumber(filter);
580804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            if (isPhoneNumber) {
580904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                phoneNumber = PhoneNumberUtils.normalizeNumber(filter);
581004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                numberE164 = PhoneNumberUtils.formatNumberToE164(phoneNumber,
581104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                        mDbHelper.getCountryIso());
581204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            }
5813174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
5814174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
5815174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        sb.append(" JOIN (SELECT " + SearchIndexColumns.CONTACT_ID + " AS snippet_contact_id");
5816174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (snippetNeeded) {
58175e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            sb.append(", ");
58185e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            if (isEmailAddress) {
58193d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append("ifnull(");
58205e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, startMatch);
582104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("||(SELECT MIN(" + Email.ADDRESS + ")");
582204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" FROM " + Tables.DATA_JOIN_RAW_CONTACTS);
582304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" WHERE  " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
582404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + RawContacts.CONTACT_ID + " AND " + Email.ADDRESS + " LIKE ");
582504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                DatabaseUtils.appendEscapedSQLString(sb, filter + "%");
582604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(")||");
58273d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, endMatch);
58283d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append(",");
58293716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
58303716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                // Optimization for single-token search.
58313716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                if (singleTokenSearch) {
58323716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    sb.append(SearchIndexColumns.CONTENT);
58333716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                } else {
58343716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
58353716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                }
58363d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append(")");
58373d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov            } else if (isPhoneNumber) {
58383d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append("ifnull(");
58393d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, startMatch);
584004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("||(SELECT MIN(" + Phone.NUMBER + ")");
584104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" FROM " +
584204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                        Tables.DATA_JOIN_RAW_CONTACTS + " JOIN " + Tables.PHONE_LOOKUP);
584304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" ON " + DataColumns.CONCRETE_ID);
584404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + Tables.PHONE_LOOKUP + "." + PhoneLookupColumns.DATA_ID);
584504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" WHERE  " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
584604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + RawContacts.CONTACT_ID);
584704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" AND " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
584804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(phoneNumber);
584904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("%'");
585004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                if (!TextUtils.isEmpty(numberE164)) {
585104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append(" OR " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
585204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append(numberE164);
585304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append("%'");
585404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                }
585504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(")||");
58565e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, endMatch);
58575e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                sb.append(",");
58583716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
58593716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                // Optimization for single-token search.
58603716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                if (singleTokenSearch) {
58613716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    sb.append(SearchIndexColumns.CONTENT);
58623716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                } else {
58633716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
58643716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                }
58655e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                sb.append(")");
586603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            } else {
586704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                final String normalizedFilter = NameNormalizer.normalize(filter);
586804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                if (!TextUtils.isEmpty(normalizedFilter)) {
58693716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    // Optimization for single-token search.
58703716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    if (singleTokenSearch) {
58713716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(SearchIndexColumns.CONTENT);
58723716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    } else {
58733716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("(CASE WHEN EXISTS (SELECT 1 FROM ");
58743716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.RAW_CONTACTS + " AS rc INNER JOIN ");
58753716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.NAME_LOOKUP + " AS nl ON (rc." + RawContacts._ID);
58763716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("=nl." + NameLookupColumns.RAW_CONTACT_ID);
58773716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(") WHERE nl." + NameLookupColumns.NORMALIZED_NAME);
58783716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(" GLOB '" + normalizedFilter + "*' AND ");
58793716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("nl." + NameLookupColumns.NAME_TYPE + "=");
58803716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(NameLookupType.NAME_COLLATION_KEY + " AND ");
58813716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
58823716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("=rc." + RawContacts.CONTACT_ID);
58833716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(") THEN NULL ELSE ");
58843716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
58853716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(" END)");
58863716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    }
588704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                } else {
588804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append("NULL");
588904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                }
589003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
58915e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            sb.append(" AS " + SearchSnippetColumns.SNIPPET);
58925e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        }
589303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
58945e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(" FROM " + Tables.SEARCH_INDEX);
58955e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(" WHERE ");
58965e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(Tables.SEARCH_INDEX + " MATCH ");
58975e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        if (isEmailAddress) {
58982352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb, "\"" + sanitizeMatch(filter) + "*\"");
58993d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov        } else if (isPhoneNumber) {
59002352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb,
590104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    "\"" + sanitizeMatch(filter) + "*\" OR \"" + phoneNumber + "*\""
59022352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                            + (numberE164 != null ? " OR \"" + numberE164 + "\"" : ""));
590303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        } else {
59042352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filter) + "*");
59059c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        }
590603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        sb.append(") ON (" + Contacts._ID + "=snippet_contact_id)");
5907a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov    }
5908a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov
59092352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov    private String sanitizeMatch(String filter) {
59102352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov        // TODO more robust preprocessing of match expressions
59112352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov        return filter.replace('-', ' ').replace('\"', ' ');
59122352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov    }
59132352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov
59145e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov    private void appendSnippetFunction(
59155e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            StringBuilder sb, String startMatch, String endMatch, String ellipsis, int maxTokens) {
59165e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append("snippet(" + Tables.SEARCH_INDEX + ",");
59175e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, startMatch);
59185e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",");
59195e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, endMatch);
59205e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",");
59215e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, ellipsis);
59225e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
59235e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        // The index of the column used for the snippet, "content"
59245e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",1,");
59255e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(maxTokens);
59265e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(")");
59275e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov    }
59285e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
5929763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForRawContacts(SQLiteQueryBuilder qb, Uri uri) {
5930763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        StringBuilder sb = new StringBuilder();
5931ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.RAW_CONTACTS);
5932763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setTables(sb.toString());
5933763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setProjectionMap(sRawContactsProjectionMap);
5934763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        appendAccountFromParameter(qb, uri);
5935763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    }
5936763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
5937a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForRawEntities(SQLiteQueryBuilder qb, Uri uri) {
5938ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        qb.setTables(Views.RAW_ENTITIES);
5939a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sRawEntityProjectionMap);
594046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        appendAccountFromParameter(qb, uri);
594146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    }
594246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
594382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
594482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String[] projection, boolean distinct) {
594546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        setTablesAndProjectionMapForData(qb, uri, projection, distinct, null);
594646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
594746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
594846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
594946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * @param usageType when non-null {@link Tables#DATA_USAGE_STAT} is joined with the specified
595046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * type.
595146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
595246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
595346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            String[] projection, boolean distinct, Integer usageType) {
595482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5955ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.DATA);
595682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sb.append(" data");
595782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
5958a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, RawContacts.CONTACT_ID);
5959a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
5960a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
5961a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
59623296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
596346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (usageType != null) {
596446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            appendDataUsageStatJoin(sb, usageType, DataColumns.CONCRETE_ID);
596546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
596646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
596782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
5968f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov
5969f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        boolean useDistinct = distinct
5970f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov                || !mDbHelper.isInProjection(projection, DISTINCT_DATA_PROHIBITING_COLUMNS);
5971f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setDistinct(useDistinct);
5972f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setProjectionMap(useDistinct ? sDistinctDataProjectionMap : sDataProjectionMap);
597382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        appendAccountFromParameter(qb, uri);
5974ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    }
5975ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
59760a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb,
59770a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String[] projection) {
59780a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5979ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.DATA);
59800a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        sb.append(" data");
5981a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
5982a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
59830a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
5984a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
5985a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sStatusUpdatesProjectionMap);
5986a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5987a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
59883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void setTablesAndProjectionMapForStreamItems(SQLiteQueryBuilder qb) {
59891dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro        qb.setTables(Tables.STREAM_ITEMS
59901dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + " JOIN " + Tables.RAW_CONTACTS + " ON ("
59911dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + StreamItemsColumns.CONCRETE_RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID
59921dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + ") JOIN " + Tables.CONTACTS + " ON ("
59931dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + RawContactsColumns.CONCRETE_CONTACT_ID + "=" + ContactsColumns.CONCRETE_ID + ")");
59943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        qb.setProjectionMap(sStreamItemsProjectionMap);
59953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
59963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
59973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void setTablesAndProjectionMapForStreamItemPhotos(SQLiteQueryBuilder qb) {
59981dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro        qb.setTables(Tables.PHOTO_FILES
59991dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + " JOIN " + Tables.STREAM_ITEM_PHOTOS + " ON ("
60001dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + StreamItemPhotosColumns.CONCRETE_PHOTO_FILE_ID + "="
60011dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + PhotoFilesColumns.CONCRETE_ID
60021dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + ") JOIN " + Tables.STREAM_ITEMS + " ON ("
60031dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "="
60041dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + StreamItemsColumns.CONCRETE_ID + ")");
60053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        qb.setProjectionMap(sStreamItemPhotosProjectionMap);
60063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
60073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
6008a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForEntities(SQLiteQueryBuilder qb, Uri uri,
6009a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection) {
6010a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
6011ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.ENTITIES);
6012a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(" data");
6013a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6014a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts.Entity.CONTACT_ID);
6015a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
6016a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, Contacts.Entity.DATA_ID);
6017a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, Contacts.Entity.DATA_ID);
6018a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6019a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
6020a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sEntityProjectionMap);
6021a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendAccountFromParameter(qb, uri);
6022a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6023a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6024a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactStatusUpdateJoin(StringBuilder sb, String[] projection,
6025a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String lastStatusUpdateIdColumn) {
6026a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection,
6027a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS,
6028a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
6029a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
6030a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
6031a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP)) {
6032a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
6033a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    + ContactsStatusUpdatesColumns.ALIAS +
6034a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + lastStatusUpdateIdColumn + "="
6035a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
60360a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
6037a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
60380a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
6039a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataStatusUpdateJoin(StringBuilder sb, String[] projection,
6040a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
6041b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
60420a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS,
60430a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_RES_PACKAGE,
60440a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_ICON,
60450a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_LABEL,
60460a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_TIMESTAMP)) {
60470a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
6048a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdatesColumns.CONCRETE_DATA_ID + "="
6049a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + dataIdColumn + ")");
60500a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
6051a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6052a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
605346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private void appendDataUsageStatJoin(StringBuilder sb, int usageType, String dataIdColumn) {
605446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        sb.append(" LEFT OUTER JOIN " + Tables.DATA_USAGE_STAT +
605546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "=" + dataIdColumn +
605646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                " AND " + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "=" + usageType + ")");
605746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
605846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
6059a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactPresenceJoin(StringBuilder sb, String[] projection,
6060a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn) {
6061a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection,
6062a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_PRESENCE, Contacts.CONTACT_CHAT_CAPABILITY)) {
6063a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
6064a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + contactIdColumn + " = "
6065a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + AggregatedPresenceColumns.CONCRETE_CONTACT_ID + ")");
6066a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
6067a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6068a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6069a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataPresenceJoin(StringBuilder sb, String[] projection,
6070a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
6071a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, Data.PRESENCE, Data.CHAT_CAPABILITY)) {
6072a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
6073a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdates.DATA_ID + "=" + dataIdColumn + ")");
6074a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
6075a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6076a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
607724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private boolean appendLocalDirectorySelectionIfNeeded(SQLiteQueryBuilder qb, long directoryId) {
6078385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directoryId == Directory.DEFAULT) {
6079385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " IN " + Tables.DEFAULT_DIRECTORY);
608024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            return true;
6081385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directoryId == Directory.LOCAL_INVISIBLE){
6082385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " NOT IN " + Tables.DEFAULT_DIRECTORY);
608324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            return true;
608424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
608524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        return false;
608624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
608724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
608824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private void appendProfileRestriction(SQLiteQueryBuilder qb, Uri uri, String profileColumn,
60896ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro            boolean andRequired, boolean suppressProfileCheck) {
60906ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro        if (!shouldIncludeProfile(uri, suppressProfileCheck)) {
609124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            qb.appendWhere((andRequired ? " AND (" : "")
609224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    + profileColumn + " IS NULL OR "
609324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    + profileColumn + "=0"
609424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    + (andRequired ? ")" : ""));
6095385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        }
6096385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov    }
6097385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov
60986ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro    private String prependProfileSortIfNeeded(Uri uri, String sortOrder,
60996ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro            boolean suppressProfileCheck) {
61006ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro        if (shouldIncludeProfile(uri, suppressProfileCheck)) {
610124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            if (TextUtils.isEmpty(sortOrder)) {
610224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                return Contacts.IS_USER_PROFILE + " DESC";
610324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            } else {
610424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                return Contacts.IS_USER_PROFILE + " DESC, " + sortOrder;
610524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
610624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
610724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        return sortOrder;
610824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
610924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
61106ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro    private boolean shouldIncludeProfile(Uri uri, boolean suppressProfileCheck) {
611124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // The user's profile may be returned alongside other contacts if it was requested and
611224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // the calling application has permission to read profile data.
6113377850d2dfd28eaf1b22273a50cfe066f6667ab9Dave Santoro        boolean profileRequested = readBooleanQueryParameter(uri, ContactsContract.ALLOW_PROFILE,
611424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                false);
61156ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro        if (profileRequested && !suppressProfileCheck) {
611624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermission(false);
611724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
611824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        return profileRequested;
611924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
612024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
61214a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) {
6122f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
6123f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
6124e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6125e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
6126e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
6127e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
6128fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
6129fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
6130e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
6131e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6132e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
6133e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
6134e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
6135e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
61364a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere(RawContacts.ACCOUNT_NAME + "="
61374a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
61384a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
61394a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountType));
61404a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        } else {
61414a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere("1");
61424a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
61434a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
61444a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
6145e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    private String appendAccountToSelection(Uri uri, String selection) {
6146f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
6147f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
6148e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6149e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
6150e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
6151e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
6152fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
6153fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
6154e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
6155e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6156e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
6157e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
6158e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
6159e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
6160e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "="
6161e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
6162e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + RawContacts.ACCOUNT_TYPE + "="
6163e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountType));
6164e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (!TextUtils.isEmpty(selection)) {
6165e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(" AND (");
6166e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(selection);
6167e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(')');
6168e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
6169e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selectionSb.toString();
6170e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } else {
6171e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selection;
6172e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
6173e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    }
6174e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
61757e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
6176c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
6177c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *
6178c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
6179c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
6180c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     */
6181f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private String getLimit(Uri uri) {
61822e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limitParam = getQueryParameter(uri, ContactsContract.LIMIT_PARAM_KEY);
6183c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limitParam == null) {
6184c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
6185c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
6186c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // make sure that the limit is a non-negative integer
6187c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        try {
6188c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int l = Integer.parseInt(limitParam);
6189c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (l < 0) {
6190c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
6191c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return null;
6192c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
6193c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return String.valueOf(l);
6194c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        } catch (NumberFormatException ex) {
6195c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
6196c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
6197c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
6198c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
6199c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
6200b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    @Override
6201f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
6202415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
6203f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (mode.equals("r")) {
6204f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            waitForAccess(mReadAccessLatch);
6205f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } else {
6206f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            waitForAccess(mWriteAccessLatch);
6207f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
6208415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
6209b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        int match = sUriMatcher.match(uri);
6210b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        switch (match) {
6211a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
6212afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                SQLiteDatabase db = mDbHelper.getReadableDatabase();
621324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
6214afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForRawContact(db, rawContactId, false);
6215afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                return openPhotoAssetFile(db, uri, mode,
621624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        Data._ID + "=" + Contacts.PHOTO_ID + " AND " +
621724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                                RawContacts.CONTACT_ID + "=?",
621824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        new String[]{String.valueOf(rawContactId)});
6219e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            }
6220b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
6221f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_ID_DISPLAY_PHOTO: {
6222f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
6223f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
6224f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Display photos retrieved by contact ID can only be read.");
6225f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6226afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                SQLiteDatabase db = mDbHelper.getReadableDatabase();
6227f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long contactId = Long.parseLong(uri.getPathSegments().get(1));
6228afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
6229afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                Cursor c = db.query(Tables.CONTACTS,
6230f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{Contacts.PHOTO_FILE_ID},
6231f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        Contacts._ID + "=?", new String[]{String.valueOf(contactId)},
6232f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        null, null, null);
6233f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
6234f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.moveToFirst();
6235f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    long photoFileId = c.getLong(0);
6236f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForRead(photoFileId);
6237f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
6238f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
6239f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6240f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6241f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6242f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_DISPLAY_PHOTO:
6243f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_ID_DISPLAY_PHOTO: {
6244f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
6245f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
6246f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Display photos retrieved by contact lookup key can only be read.");
6247f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6248f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                List<String> pathSegments = uri.getPathSegments();
6249f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                int segmentCount = pathSegments.size();
6250f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (segmentCount < 4) {
6251f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
6252f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Missing a lookup key", uri));
6253f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6254afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                SQLiteDatabase db = mDbHelper.getReadableDatabase();
6255f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                String lookupKey = pathSegments.get(2);
6256f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                String[] projection = new String[]{Contacts.PHOTO_FILE_ID};
6257f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (segmentCount == 5) {
6258f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    long contactId = Long.parseLong(pathSegments.get(3));
6259afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                    enforceProfilePermissionForContact(db, contactId, false);
6260f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
6261f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
6262afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
6263f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            projection, null, null, null, null, null,
6264f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
6265f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (c != null) {
6266f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        try {
6267f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            c.moveToFirst();
6268f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            long photoFileId = c.getLong(c.getColumnIndex(Contacts.PHOTO_FILE_ID));
6269f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            return openDisplayPhotoForRead(photoFileId);
6270f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        } finally {
6271f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            c.close();
6272f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
6273f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
6274f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6275f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6276f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
6277f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                setTablesAndProjectionMapForContacts(qb, uri, projection);
6278afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                long contactId = lookupContactIdByLookupKey(db, lookupKey);
6279afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
6280afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                Cursor c = qb.query(db, projection, Contacts._ID + "=?",
6281f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{String.valueOf(contactId)}, null, null, null);
6282f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
6283f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.moveToFirst();
6284f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    long photoFileId = c.getLong(c.getColumnIndex(Contacts.PHOTO_FILE_ID));
6285f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForRead(photoFileId);
6286f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
6287f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
6288f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6289f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6290f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6291f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case RAW_CONTACTS_ID_DISPLAY_PHOTO: {
6292f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
6293f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                boolean writeable = !mode.equals("r");
6294afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                SQLiteDatabase db = mDbHelper.getReadableDatabase();
6295afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForRawContact(db, rawContactId, writeable);
6296f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6297f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Find the primary photo data record for this raw contact.
6298f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
6299f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                String[] projection = new String[]{Data._ID, Photo.PHOTO_FILE_ID};
6300f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
6301afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                Cursor c = qb.query(db, projection,
6302f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        Data.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?",
6303f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{String.valueOf(rawContactId), Photo.CONTENT_ITEM_TYPE},
6304f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        null, null, Data.IS_PRIMARY + " DESC");
6305f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long dataId = 0;
6306f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long photoFileId = 0;
6307f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
6308f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (c.getCount() >= 1) {
6309f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        c.moveToFirst();
6310f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        dataId = c.getLong(0);
6311f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        photoFileId = c.getLong(1);
6312f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
6313f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
6314f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
6315f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6316f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6317f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // If writeable, open a writeable file descriptor that we can monitor.
6318f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // When the caller finishes writing content, we'll process the photo and
6319f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // update the data record.
6320f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (writeable) {
6321f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForWrite(rawContactId, dataId, uri, mode);
6322f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } else {
6323f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForRead(photoFileId);
6324f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6325f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6326f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6327f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case DISPLAY_PHOTO: {
6328f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long photoFileId = ContentUris.parseId(uri);
6329f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
6330f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
6331f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Display photos retrieved by key can only be read.");
6332f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6333f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                return openDisplayPhotoForRead(photoFileId);
6334f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6335f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6336e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            case DATA_ID: {
6337afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                SQLiteDatabase db = mDbHelper.getReadableDatabase();
633824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long dataId = Long.parseLong(uri.getPathSegments().get(1));
6339afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForData(db, dataId, false);
6340afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                return openPhotoAssetFile(db, uri, mode,
6341e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        Data._ID + "=? AND " + Data.MIMETYPE + "='" + Photo.CONTENT_ITEM_TYPE + "'",
634224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        new String[]{String.valueOf(dataId)});
6343d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
6344d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6345fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            case PROFILE_AS_VCARD: {
6346fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // When opening a contact as file, we pass back contents as a
6347fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // vCard-encoded stream. We build into a local buffer first,
6348fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // then pipe into MemoryFile once the exact size is known.
6349fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
6350fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(uri, localStream, null, null);
6351fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                return buildAssetFileDescriptor(localStream);
6352fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            }
635342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
6354fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            case CONTACTS_AS_VCARD: {
635542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // When opening a contact as file, we pass back contents as a
635642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // vCard-encoded stream. We build into a local buffer first,
635742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // then pipe into MemoryFile once the exact size is known.
635842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
6359fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(uri, localStream, null, null);
6360f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
636142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
636242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
636342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
636449d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                SQLiteDatabase db = mDbHelper.getReadableDatabase();
636542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKeys = uri.getPathSegments().get(2);
636642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String[] loopupKeyList = lookupKeys.split(":");
636742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final StringBuilder inBuilder = new StringBuilder();
6368fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                Uri queryUri = Contacts.CONTENT_URI;
636942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                int index = 0;
6370fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen
6371d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // SQLite has limits on how many parameters can be used
6372d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // so the IDs are concatenated to a query string here instead
637342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                for (String lookupKey : loopupKeyList) {
637442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    if (index == 0) {
6375d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append("(");
637642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    } else {
6377d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append(",");
637842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    }
637924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    long contactId = lookupContactIdByLookupKey(db, lookupKey);
6380afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                    enforceProfilePermissionForContact(db, contactId, false);
638124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    inBuilder.append(contactId);
6382fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                    if (mProfileIdCache.profileContactId == contactId) {
6383fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                        queryUri = queryUri.buildUpon().appendQueryParameter(
6384377850d2dfd28eaf1b22273a50cfe066f6667ab9Dave Santoro                                ContactsContract.ALLOW_PROFILE, "true").build();
6385fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                    }
638642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    index++;
638742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                }
638842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                inBuilder.append(')');
638942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + " IN " + inBuilder.toString();
6390d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6391d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // When opening a contact as file, we pass back contents as a
6392d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // vCard-encoded stream. We build into a local buffer first,
6393d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // then pipe into MemoryFile once the exact size is known.
6394d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
6395fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(queryUri, localStream, selection, null);
6396f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
6397d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
6398b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
6399b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            default:
6400fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new FileNotFoundException(mDbHelper.exceptionMessage("File does not exist",
6401fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        uri));
6402b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        }
6403b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
6404b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
6405afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro    private AssetFileDescriptor openPhotoAssetFile(SQLiteDatabase db, Uri uri, String mode,
6406afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro            String selection, String[] selectionArgs)
6407e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throws FileNotFoundException {
6408e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        if (!"r".equals(mode)) {
6409e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throw new FileNotFoundException(mDbHelper.exceptionMessage("Mode " + mode
6410e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                    + " not supported.", uri));
6411e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        }
6412e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
6413e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        String sql =
6414ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                "SELECT " + Photo.PHOTO + " FROM " + Views.DATA +
6415e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                " WHERE " + selection;
641608ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        try {
6417f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
6418f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    DatabaseUtils.blobFileDescriptorForQuery(db, sql, selectionArgs));
641908ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        } catch (SQLiteDoneException e) {
642008ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            // this will happen if the DB query returns no rows (i.e. contact does not exist)
642108ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            throw new FileNotFoundException(uri.toString());
642208ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        }
6423e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov    }
6424e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
6425f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
6426f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Opens a display photo from the photo store for reading.
6427f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param photoFileId The display photo file ID
6428f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @return An asset file descriptor that allows the file to be read.
6429f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @throws FileNotFoundException If no photo file for the given ID exists.
6430f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
6431f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private AssetFileDescriptor openDisplayPhotoForRead(long photoFileId)
6432f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            throws FileNotFoundException {
6433f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        PhotoStore.Entry entry = mPhotoStore.get(photoFileId);
6434f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (entry != null) {
6435f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            return makeAssetFileDescriptor(
6436f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    ParcelFileDescriptor.open(new File(entry.path),
6437f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            ParcelFileDescriptor.MODE_READ_ONLY),
6438f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    entry.size);
6439f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } else {
6440f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
6441f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            throw new FileNotFoundException("No photo file found for ID " + photoFileId);
6442f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
6443f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
6444f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6445f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
6446f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Opens a file descriptor for a photo to be written.  When the caller completes writing
6447f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * to the file (closing the output stream), the image will be parsed out and processed.
6448f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * If processing succeeds, the given raw contact ID's primary photo record will be
6449f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * populated with the inserted image (if no primary photo record exists, the data ID can
6450f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * be left as 0, and a new data record will be inserted).
6451f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param rawContactId Raw contact ID this photo entry should be associated with.
6452f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param dataId Data ID for a photo mimetype that will be updated with the inserted
6453f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     image.  May be set to 0, in which case the inserted image will trigger creation
6454f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     of a new primary photo image data row for the raw contact.
6455f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param uri The URI being used to access this file.
6456f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param mode Read/write mode string.
6457f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @return An asset file descriptor the caller can use to write an image file for the
6458f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     raw contact.
6459f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
6460f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private AssetFileDescriptor openDisplayPhotoForWrite(long rawContactId, long dataId, Uri uri,
6461f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            String mode) {
6462f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        try {
6463f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            return new AssetFileDescriptor(new MonitoredParcelFileDescriptor(rawContactId, dataId,
6464f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    ParcelFileDescriptor.open(File.createTempFile("img", null),
6465f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            ContentResolver.modeToMode(uri, mode))),
6466f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    0, AssetFileDescriptor.UNKNOWN_LENGTH);
6467f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } catch (IOException ioe) {
6468f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            Log.e(TAG, "Could not create temp image file in mode " + mode);
6469f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            return null;
6470f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
6471f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
6472f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6473f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
6474f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Parcel file descriptor wrapper that monitors when the file is closed.
6475f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * If the file contains a valid image, the image is either inserted into the given
6476f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * raw contact or updated in the given data row.
6477f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
6478f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private class MonitoredParcelFileDescriptor extends ParcelFileDescriptor {
6479f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        private final long mRawContactId;
6480f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        private final long mDataId;
6481f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        private MonitoredParcelFileDescriptor(long rawContactId, long dataId,
6482f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                ParcelFileDescriptor descriptor) {
6483f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            super(descriptor);
6484f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            mRawContactId = rawContactId;
6485f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            mDataId = dataId;
6486f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
6487f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6488f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        @Override
6489f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        public void close() throws IOException {
6490f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            try {
6491f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Check to see whether a valid image was written out.
6492f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                Bitmap b = BitmapFactory.decodeFileDescriptor(getFileDescriptor());
6493f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (b != null) {
6494f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    PhotoProcessor processor = new PhotoProcessor(b, mMaxDisplayPhotoDim,
6495f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            mMaxThumbnailPhotoDim);
6496f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6497f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    // Store the compressed photo in the photo store.
6498f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    long photoFileId = mPhotoStore.insert(processor);
6499f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6500f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    // Depending on whether we already had a data row to attach the photo to,
6501f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    // do an update or insert.
6502f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (mDataId != 0) {
6503f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Update the data record with the new photo.
6504f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        ContentValues updateValues = new ContentValues();
6505f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6506f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Signal that photo processing has already been handled.
6507f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        updateValues.put(DataRowHandlerForPhoto.SKIP_PROCESSING_KEY, true);
6508f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6509f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        if (photoFileId != 0) {
6510f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            updateValues.put(Photo.PHOTO_FILE_ID, photoFileId);
6511f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
6512f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        updateValues.put(Photo.PHOTO, processor.getThumbnailPhotoBytes());
6513f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        update(ContentUris.withAppendedId(Data.CONTENT_URI, mDataId), updateValues,
6514f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                null, null);
6515f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    } else {
6516f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Insert a new primary data record with the photo.
6517f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        ContentValues insertValues = new ContentValues();
6518f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6519f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Signal that photo processing has already been handled.
6520f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(DataRowHandlerForPhoto.SKIP_PROCESSING_KEY, true);
6521f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6522f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
6523f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Data.IS_PRIMARY, 1);
6524f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        if (photoFileId != 0) {
6525f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            insertValues.put(Photo.PHOTO_FILE_ID, photoFileId);
6526f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
6527f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Photo.PHOTO, processor.getThumbnailPhotoBytes());
6528f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insert(RawContacts.CONTENT_URI.buildUpon()
6529f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                .appendPath(String.valueOf(mRawContactId))
6530f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                .appendPath(RawContacts.Data.CONTENT_DIRECTORY).build(),
6531f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                insertValues);
6532f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
6533f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6534f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            } finally {
6535f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                super.close();
6536f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6537f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
6538f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
6539f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6540d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String CONTACT_MEMORY_FILE_NAME = "contactAssetFile";
6541d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6542d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
6543f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert     * Returns an {@link AssetFileDescriptor} backed by the
6544d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * contents of the given {@link ByteArrayOutputStream}.
6545d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
6546f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor buildAssetFileDescriptor(ByteArrayOutputStream stream) {
6547d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        try {
6548d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            stream.flush();
6549d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6550d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final byte[] byteData = stream.toByteArray();
6551d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6552f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
6553f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    ParcelFileDescriptor.fromData(byteData, CONTACT_MEMORY_FILE_NAME),
6554f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    byteData.length);
6555d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        } catch (IOException e) {
6556ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            Log.w(TAG, "Problem writing stream into an ParcelFileDescriptor: " + e.toString());
6557ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            return null;
6558d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
6559d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
6560d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6561f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd) {
6562f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return makeAssetFileDescriptor(fd, AssetFileDescriptor.UNKNOWN_LENGTH);
6563f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
6564f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
6565f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd, long length) {
6566f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return fd != null ? new AssetFileDescriptor(fd, 0, length) : null;
6567f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
6568f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
6569d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
6570d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Output {@link RawContacts} matching the requested selection in the vCard
6571d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * format to the given {@link OutputStream}. This method returns silently if
6572d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * any errors encountered.
6573d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
6574fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen    private void outputRawContactsAsVCard(Uri uri, OutputStream stream,
6575fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            String selection, String[] selectionArgs) {
6576d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final Context context = this.getContext();
6577dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen        int vcardconfig = VCardConfig.VCARD_TYPE_DEFAULT;
6578fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen        if(uri.getBooleanQueryParameter(
6579fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                Contacts.QUERY_PARAMETER_VCARD_NO_PHOTO, false)) {
6580dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen            vcardconfig |= VCardConfig.FLAG_REFRAIN_IMAGE_EXPORT;
6581dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen        }
65827a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        final VCardComposer composer =
6583dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen                new VCardComposer(context, vcardconfig, false);
6584108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        Writer writer = null;
6585108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        try {
6586108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            writer = new BufferedWriter(new OutputStreamWriter(stream));
6587fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            if (!composer.init(uri, selection, selectionArgs, null)) {
6588108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                Log.w(TAG, "Failed to init VCardComposer");
6589108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                return;
6590108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            }
6591d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6592108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            while (!composer.isAfterLast()) {
6593108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                writer.write(composer.createOneEntry());
6594108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            }
6595108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        } catch (IOException e) {
6596108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            Log.e(TAG, "IOException: " + e);
6597108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        } finally {
6598108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            composer.terminate();
6599108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            if (writer != null) {
6600108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                try {
6601108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                    writer.close();
6602108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                } catch (IOException e) {
6603108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                    Log.w(TAG, "IOException during closing output stream: " + e);
6604108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                }
6605d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
6606d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
6607d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
6608b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
66094f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
66104f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
6611415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
6612415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov        waitForAccess(mReadAccessLatch);
6613415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
6614a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
66154f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
6616b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS:
6617be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return Contacts.CONTENT_TYPE;
66182d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill            case CONTACTS_LOOKUP:
6619b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_ID:
6620b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
662124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE:
6622b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_ITEM_TYPE;
6623f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD:
662442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD:
662524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD:
6626f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                return Contacts.CONTENT_VCARD_TYPE;
6627f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            case CONTACTS_ID_PHOTO:
6628f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_ID_DISPLAY_PHOTO:
6629f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_DISPLAY_PHOTO:
6630f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_ID_DISPLAY_PHOTO:
6631f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case RAW_CONTACTS_ID_DISPLAY_PHOTO:
6632f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case DISPLAY_PHOTO:
6633f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                return "image/jpeg";
6634b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS:
663524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS:
6636be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return RawContacts.CONTENT_TYPE;
6637b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS_ID:
663824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID:
6639b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return RawContacts.CONTENT_ITEM_TYPE;
6640f481f22a9323fe338672f99b88b26c5f0725cd42David Brown            case DATA:
664124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA:
6642f481f22a9323fe338672f99b88b26c5f0725cd42David Brown                return Data.CONTENT_TYPE;
6643508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
6644b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getDataMimeType(ContentUris.parseId(uri));
664548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES:
664648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_TYPE;
664748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
664848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_ITEM_TYPE;
66499005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov            case PHONE_LOOKUP:
66509005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov                return PhoneLookup.CONTENT_TYPE;
665148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS:
665248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_TYPE;
665348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
665448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_ITEM_TYPE;
665548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS:
665648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_TYPE;
665748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID:
665848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_ITEM_TYPE;
6659b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
6660b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_TYPE;
6661b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
6662b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_ITEM_TYPE;
6663b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case SETTINGS:
6664b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Settings.CONTENT_TYPE;
6665b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
6666b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_TYPE;
6667c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS:
6668c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SUGGEST_MIME_TYPE;
6669c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT:
6670c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
6671d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES:
6672d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_TYPE;
6673d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID:
6674d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_ITEM_TYPE;
667561efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov            default:
667661efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov                return mLegacyApiSupport.getType(uri);
66774f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
66784f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
66797e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
668009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    public String[] getDefaultProjection(Uri uri) {
668109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        final int match = sUriMatcher.match(uri);
668209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        switch (match) {
668309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS:
668409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP:
668509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_ID:
668609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
668709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
668824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE:
668909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsProjectionMap.getColumnNames();
669009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
66918727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov            case CONTACTS_ID_ENTITIES:
669224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_ENTITIES:
66938727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov                return sEntityProjectionMap.getColumnNames();
66948727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov
669509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_VCARD:
669609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_MULTI_VCARD:
669724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD:
669809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsVCardProjectionMap.getColumnNames();
669909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
670009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS:
670109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS_ID:
670224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS:
670324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID:
670409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sRawContactsProjectionMap.getColumnNames();
670509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
670609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DATA_ID:
670709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES:
670809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES_ID:
670909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS:
671009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS_ID:
671109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS:
671209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS_ID:
671324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA:
671409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDataProjectionMap.getColumnNames();
671509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
671609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONE_LOOKUP:
671709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sPhoneLookupProjectionMap.getColumnNames();
671809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
671909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
672009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
672109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sAggregationExceptionsProjectionMap.getColumnNames();
672209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
672309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case SETTINGS:
672409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sSettingsProjectionMap.getColumnNames();
672509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
672609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES:
672709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES_ID:
672809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDirectoryProjectionMap.getColumnNames();
672909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
673009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            default:
673109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return null;
673209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
673309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    }
673409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
6735f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private class StructuredNameLookupBuilder extends NameLookupBuilder {
6736f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6737f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public StructuredNameLookupBuilder(NameSplitter splitter) {
6738f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            super(splitter);
6739f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6740f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6741f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
6742f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
6743f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name) {
674478fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov            mDbHelper.insertNameLookup(rawContactId, dataId, lookupType, name);
6745f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6746f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6747f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
6748f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected String[] getCommonNicknameClusters(String normalizedName) {
6749d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov            return mCommonNicknameCache.getCommonNicknameClusters(normalizedName);
6750f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6751f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
6752f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
67532d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov    public void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
6754d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
6755d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + RawContacts.CONTACT_ID +
6756d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.RAW_CONTACTS +
6757d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " JOIN " + Tables.NAME_LOOKUP +
6758d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " ON(" + RawContactsColumns.CONCRETE_ID + "="
6759d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
6760d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE normalized_name GLOB '");
6761e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
6762916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE +
6763916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " IN(" + CONTACT_LOOKUP_NAME_TYPES + "))");
6764e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    }
6765e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
67669a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    public boolean isPhoneNumber(String filter) {
67679a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        boolean atLeastOneDigit = false;
67689a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        int len = filter.length();
67699a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        for (int i = 0; i < len; i++) {
67709a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            char c = filter.charAt(i);
67719a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            if (c >= '0' && c <= '9') {
67729a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                atLeastOneDigit = true;
67739a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            } else if (c != '*' && c != '#' && c != '+' && c != 'N' && c != '.' && c != ';'
67749a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                    && c != '-' && c != '(' && c != ')' && c != ' ') {
67759a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                return false;
67769a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            }
67779a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
67789a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return atLeastOneDigit;
67799a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
67809a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
67814a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /**
67827a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * Takes components of a name from the query parameters and returns a cursor with those
67837a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * components as well as all missing components.  There is no database activity involved
67847a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * in this so the call can be made on the UI thread.
67857a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     */
67867a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private Cursor completeName(Uri uri, String[] projection) {
67877a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        if (projection == null) {
67887a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            projection = sDataProjectionMap.getColumnNames();
67897a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
67907a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
67917a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        ContentValues values = new ContentValues();
6792f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        DataRowHandlerForStructuredName handler = (DataRowHandlerForStructuredName)
6793f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                getDataRowHandler(StructuredName.CONTENT_ITEM_TYPE);
67947a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
67957a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        copyQueryParamsToContentValues(values, uri,
67967a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.DISPLAY_NAME,
67977a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PREFIX,
67987a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.GIVEN_NAME,
67997a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.MIDDLE_NAME,
68007a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.FAMILY_NAME,
68017a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.SUFFIX,
68027a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_NAME,
68037a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_FAMILY_NAME,
68047a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_MIDDLE_NAME,
68057a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_GIVEN_NAME
68067a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        );
68077a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
68087a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        handler.fixStructuredNameComponents(values, values);
68097a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
68107a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
68117a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        Object[] row = new Object[projection.length];
68127a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
68137a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            row[i] = values.get(projection[i]);
68147a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
68157a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        cursor.addRow(row);
68167a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        return cursor;
68177a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
68187a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
68197a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private void copyQueryParamsToContentValues(ContentValues values, Uri uri, String... columns) {
68207a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (String column : columns) {
68217a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            String param = uri.getQueryParameter(column);
68227a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            if (param != null) {
68237a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                values.put(column, param);
68247a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
68257a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
68267a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
68277a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
68287a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
68297a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    /**
68304a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     * Inserts an argument at the beginning of the selection arg list.
68314a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     */
68324a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private String[] insertSelectionArg(String[] selectionArgs, String arg) {
6833b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
6834b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
6835b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
6836b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
6837b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
68384a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            newSelectionArgs[0] = arg;
68394a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length);
6840b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
6841b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
6842b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
6843caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
68445e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private String[] appendProjectionArg(String[] projection, String arg) {
68455e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        if (projection == null) {
68465e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            return null;
68475e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        }
68485e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        final int length = projection.length;
68495e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        String[] newProjection = new String[length + 1];
68505e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        System.arraycopy(projection, 0, newProjection, 0, length);
68515e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        newProjection[length] = arg;
68525e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        return newProjection;
68535e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    }
68545e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
6855caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    protected Account getDefaultAccount() {
6856caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        AccountManager accountManager = AccountManager.get(getContext());
6857caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        try {
68585f1f4a062ac34d75d2dbf586702cbeb121cf09caDmitri Plotnikov            Account[] accounts = accountManager.getAccountsByType(DEFAULT_ACCOUNT_TYPE);
6859caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            if (accounts != null && accounts.length > 0) {
6860caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov                return accounts[0];
6861caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            }
6862caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        } catch (Throwable e) {
68636f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov            Log.e(TAG, "Cannot determine the default account for contacts compatibility", e);
6864caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        }
68656f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov        return null;
6866caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    }
6867f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
686873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    /**
686973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     * Returns true if the specified account type is writable.
687073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     */
687173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    protected boolean isWritableAccount(String accountType) {
6872bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        if (accountType == null) {
6873bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov            return true;
6874bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        }
6875bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov
687673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        Boolean writable = mAccountWritability.get(accountType);
687773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable != null) {
687873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            return writable;
687973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
688073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
6881627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        IContentService contentService = ContentResolver.getContentService();
6882627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
6883627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (SyncAdapterType sync : contentService.getSyncAdapterTypes()) {
6884627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (ContactsContract.AUTHORITY.equals(sync.authority) &&
688573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                        accountType.equals(sync.accountType)) {
688673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    writable = sync.supportsUploading();
688773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    break;
6888627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
6889627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
6890627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } catch (RemoteException e) {
6891627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            Log.e(TAG, "Could not acquire sync adapter types");
6892627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
689373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
689473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable == null) {
689573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            writable = false;
689673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
689773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
689873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.put(accountType, writable);
689973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        return writable;
6900627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
6901b4e61e064b59e8076df81b061add9fb358fd2ed9Dmitri Plotnikov
6902d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
6903f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static boolean readBooleanQueryParameter(Uri uri, String parameter,
6904f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean defaultValue) {
6905f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6906f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        // Manually parse the query, which is much faster than calling uri.getQueryParameter
6907f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
6908f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
6909f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
6910f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6911f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6912f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = query.indexOf(parameter);
6913f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (index == -1) {
6914f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
6915f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6916f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6917f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        index += parameter.length();
6918f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6919f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return !matchQueryParameter(query, index, "=0", false)
6920f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && !matchQueryParameter(query, index, "=false", true);
6921f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
6922f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6923f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private static boolean matchQueryParameter(String query, int index, String value,
6924f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean ignoreCase) {
6925f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int length = value.length();
6926f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return query.regionMatches(ignoreCase, index, value, 0, length)
6927f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && (query.length() == index + length || query.charAt(index + length) == '&');
6928f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
6929f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6930f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /**
6931f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * A fast re-implementation of {@link Uri#getQueryParameter}
6932f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     */
6933f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static String getQueryParameter(Uri uri, String parameter) {
6934f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
6935f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
6936f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return null;
6937f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6938f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6939f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int queryLength = query.length();
6940f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int parameterLength = parameter.length();
6941f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6942f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String value;
6943f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = 0;
6944f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        while (true) {
6945f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index = query.indexOf(parameter, index);
6946f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (index == -1) {
6947f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
69485fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            }
69495fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa
69505fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            // Should match against the whole parameter instead of its suffix.
69515fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            // e.g. The parameter "param" must not be found in "some_param=val".
69525fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            if (index > 0) {
69535fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                char prevChar = query.charAt(index - 1);
69545fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                if (prevChar != '?' && prevChar != '&') {
69555fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    // With "some_param=val1&param=val2", we should find second "param" occurrence.
69565fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    index += parameterLength;
69575fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    continue;
69585fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                }
6959f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
6960f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6961f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index += parameterLength;
6962f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6963f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (queryLength == index) {
6964f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
6965f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
6966f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6967f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (query.charAt(index) == '=') {
6968f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                index++;
6969f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                break;
6970f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
6971f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6972f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6973f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int ampIndex = query.indexOf('&', index);
6974f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (ampIndex == -1) {
6975f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index);
6976f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        } else {
6977f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index, ampIndex);
6978f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6979f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6980f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return Uri.decode(value);
6981f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
69825dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
69830dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    protected boolean isAggregationUpgradeNeeded() {
69840dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
69850dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            return false;
69860dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
69870dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
69880dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int version = Integer.parseInt(mDbHelper.getProperty(PROPERTY_AGGREGATION_ALGORITHM, "1"));
69890dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        return version < PROPERTY_AGGREGATION_ALGORITHM_VERSION;
69900dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
69910dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
6992bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void upgradeAggregationAlgorithmInBackground() {
69930dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // This upgrade will affect very few contacts, so it can be performed on the
69940dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // main thread during the initial boot after an OTA
69950dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
69960dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        Log.i(TAG, "Upgrading aggregation algorithm");
69970dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int count = 0;
69980dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        long start = SystemClock.currentThreadTimeMillis();
69990dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        try {
700049d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov            mDb = mDbHelper.getWritableDatabase();
70010dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.beginTransaction();
70020dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            Cursor cursor = mDb.query(true,
70030dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    Tables.RAW_CONTACTS + " r1 JOIN " + Tables.RAW_CONTACTS + " r2",
70040dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    new String[]{"r1." + RawContacts._ID},
70050dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    "r1." + RawContacts._ID + "!=r2." + RawContacts._ID +
70060dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.CONTACT_ID + "=r2." + RawContacts.CONTACT_ID +
70070dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.ACCOUNT_NAME + "=r2." + RawContacts.ACCOUNT_NAME +
70080dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.ACCOUNT_TYPE + "=r2." + RawContacts.ACCOUNT_TYPE,
70090dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    null, null, null, null, null);
70100dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            try {
70110dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                while (cursor.moveToNext()) {
70120dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    long rawContactId = cursor.getLong(0);
70130dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId,
70140dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                            RawContacts.AGGREGATION_MODE_DEFAULT, true);
70150dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    count++;
70160dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                }
70170dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            } finally {
70180dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                cursor.close();
70190dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            }
7020bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mContactAggregator.aggregateInTransaction(mTransactionContext, mDb);
7021bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            updateSearchIndexInTransaction();
70220dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.setTransactionSuccessful();
70230dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDbHelper.setProperty(PROPERTY_AGGREGATION_ALGORITHM,
70240dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    String.valueOf(PROPERTY_AGGREGATION_ALGORITHM_VERSION));
70250dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        } finally {
70260dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.endTransaction();
70270dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            long end = SystemClock.currentThreadTimeMillis();
70280dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            Log.i(TAG, "Aggregation algorithm upgraded for " + count
70290dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    + " contacts, in " + (end - start) + "ms");
70300dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
70310dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
70329a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
70339a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    /* Visible for testing */
70349a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    boolean isPhone() {
70359a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (!sIsPhoneInitialized) {
70369a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sIsPhone = new TelephonyManager(getContext()).isVoiceCapable();
70379a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sIsPhoneInitialized = true;
70389a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
70399a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return sIsPhone;
70409a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
704146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
704246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private boolean handleDataUsageFeedback(Uri uri) {
704346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final long currentTimeMillis = System.currentTimeMillis();
704446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String usageType = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
704546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] ids = uri.getLastPathSegment().trim().split(",");
704646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final ArrayList<Long> dataIds = new ArrayList<Long>();
704746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
704846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        for (String id : ids) {
704946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            dataIds.add(Long.valueOf(id));
705046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
705146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final boolean successful;
705246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (TextUtils.isEmpty(usageType)) {
705346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            Log.w(TAG, "Method for data usage feedback isn't specified. Ignoring.");
705446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            successful = false;
705546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } else {
705646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            successful = updateDataUsageStat(dataIds, usageType, currentTimeMillis) > 0;
705746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
705846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
705946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        // Handle old API. This doesn't affect the result of this entire method.
706046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] questionMarks = new String[ids.length];
706146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        Arrays.fill(questionMarks, "?");
706246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String where = Data._ID + " IN (" + TextUtils.join(",", questionMarks) + ")";
706346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final Cursor cursor = mDb.query(
7064ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                Views.DATA,
706546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                new String[] { Data.CONTACT_ID },
706646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                where, ids, null, null, null);
706746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        try {
706846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            while (cursor.moveToNext()) {
706946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mSelectionArgs1[0] = cursor.getString(0);
707046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                ContentValues values2 = new ContentValues();
707146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                values2.put(Contacts.LAST_TIME_CONTACTED, currentTimeMillis);
707246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDb.update(Tables.CONTACTS, values2, Contacts._ID + "=?", mSelectionArgs1);
707346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDb.execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
707446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDb.execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
707546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
707646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } finally {
707746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            cursor.close();
707846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
707946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
708046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        return successful;
708146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
708246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
708346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
708446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Update {@link Tables#DATA_USAGE_STAT}.
708546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     *
708646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * @return the number of rows affected.
708746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
7088f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa    @VisibleForTesting
7089f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa    /* package */ int updateDataUsageStat(
7090f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa            List<Long> dataIds, String type, long currentTimeMillis) {
709146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final int typeInt = sDataUsageTypeMap.get(type);
709246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String where = DataUsageStatColumns.DATA_ID + " =? AND "
709346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                + DataUsageStatColumns.USAGE_TYPE_INT + " =?";
709446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] columns =
709546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                new String[] { DataUsageStatColumns._ID, DataUsageStatColumns.TIMES_USED };
709646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final ContentValues values = new ContentValues();
709746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        for (Long dataId : dataIds) {
709846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            final String[] args = new String[] { dataId.toString(), String.valueOf(typeInt) };
709946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            mDb.beginTransaction();
710046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            try {
710146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                final Cursor cursor = mDb.query(Tables.DATA_USAGE_STAT, columns, where, args,
710246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        null, null, null);
710346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                try {
710446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (cursor.getCount() > 0) {
710546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        if (!cursor.moveToFirst()) {
710646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            Log.e(TAG,
710746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    "moveToFirst() failed while getAccount() returned non-zero.");
710846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        } else {
710946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.clear();
711046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.put(DataUsageStatColumns.TIMES_USED, cursor.getInt(1) + 1);
711146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.put(DataUsageStatColumns.LAST_TIME_USED, currentTimeMillis);
711246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            mDb.update(Tables.DATA_USAGE_STAT, values,
711346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    DataUsageStatColumns._ID + " =?",
711446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    new String[] { cursor.getString(0) });
711546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        }
711646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    } else {
711746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.clear();
711846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.DATA_ID, dataId);
711946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.USAGE_TYPE_INT, typeInt);
712046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.TIMES_USED, 1);
712146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.LAST_TIME_USED, currentTimeMillis);
712246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        mDb.insert(Tables.DATA_USAGE_STAT, null, values);
712346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    }
712446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    mDb.setTransactionSuccessful();
712546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                } finally {
712646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    cursor.close();
712746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
712846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            } finally {
712946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDb.endTransaction();
713046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
713146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
713246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
713346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        return dataIds.size();
713446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
713546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
713646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
713746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Returns a sort order String for promoting data rows (email addresses, phone numbers, etc.)
713846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * associated with a primary account. The primary account should be supplied from applications
713946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * with {@link ContactsContract#PRIMARY_ACCOUNT_NAME} and
714046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * {@link ContactsContract#PRIMARY_ACCOUNT_TYPE}. Null will be returned when the primary
714146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * account isn't available.
714246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
714346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private String getAccountPromotionSortOrder(Uri uri) {
714446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String primaryAccountName =
714546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME);
714646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String primaryAccountType =
714746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE);
714846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
714946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        // Data rows associated with primary account should be promoted.
715046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (!TextUtils.isEmpty(primaryAccountName)) {
715146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            StringBuilder sb = new StringBuilder();
715246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            sb.append("(CASE WHEN " + RawContacts.ACCOUNT_NAME + "=");
715346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            DatabaseUtils.appendEscapedSQLString(sb, primaryAccountName);
715446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            if (!TextUtils.isEmpty(primaryAccountType)) {
715546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                sb.append(" AND " + RawContacts.ACCOUNT_TYPE + "=");
715646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DatabaseUtils.appendEscapedSQLString(sb, primaryAccountType);
715746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
715846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            sb.append(" THEN 0 ELSE 1 END)");
715946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            return sb.toString();
716046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } else {
716146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            return null;
716246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
716346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
71644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
7165