ContactsProvider2.java revision 4b64b6e8f448938434cb1e022a4e7dfaae8f9c8c
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,
839f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                    "(SELECT COUNT(" + ContactsColumns.CONCRETE_ID + ") FROM "
840f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + Tables.CONTACTS_JOIN_RAW_CONTACTS_DATA_FILTERED_BY_GROUPMEMBERSHIP
841f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + ")")
842f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_WITH_PHONES,
843f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                    "(SELECT COUNT(" + ContactsColumns.CONCRETE_ID + ") FROM "
844f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + Tables.CONTACTS_JOIN_RAW_CONTACTS_DATA_FILTERED_BY_GROUPMEMBERSHIP
845f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + " WHERE " + Contacts.HAS_PHONE_NUMBER + ")")
846f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            .build();
847f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa
848f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa    // This is only exposed as hidden API for the contacts app, so we can be very specific in
849f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa    // the filtering
850f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa    private static final ProjectionMap sGroupsSummaryProjectionMapWithGroupCountPerAccount =
851f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            ProjectionMap.builder()
852f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            .addAll(sGroupsSummaryProjectionMap)
853f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            .add(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT,
854f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                    "(SELECT COUNT(*) FROM " + Views.GROUPS + " WHERE "
855f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + "(" + Groups.ACCOUNT_NAME + "="
856f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + GroupsColumns.CONCRETE_ACCOUNT_NAME
857f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + " AND "
858f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.ACCOUNT_TYPE + "=" + GroupsColumns.CONCRETE_ACCOUNT_TYPE
859f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + " AND "
860f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.DELETED + "=0 AND "
861f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.FAVORITES + "=0 AND "
862f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.AUTO_ADD + "=0"
863f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + ")"
864f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + " GROUP BY "
865f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.ACCOUNT_NAME + ", " + Groups.ACCOUNT_TYPE
866f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                   + ")")
867f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
868f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
869373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains the agg_exceptions columns */
870f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sAggregationExceptionsProjectionMap = ProjectionMap.builder()
871f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id")
872f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.TYPE)
873f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID1)
874f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID2)
875f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
876f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
877eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    /** Contains the agg_exceptions columns */
878f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSettingsProjectionMap = ProjectionMap.builder()
879f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_NAME)
880f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_TYPE)
881f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_VISIBLE)
882f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.SHOULD_SYNC)
883f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ANY_UNSYNCED,
884f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN MIN(" + Settings.SHOULD_SYNC
885f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                        + ",(SELECT "
886f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + "(CASE WHEN MIN(" + Groups.SHOULD_SYNC + ") IS NULL"
887f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " THEN 1"
888f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " ELSE MIN(" + Groups.SHOULD_SYNC + ")"
889f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " END)"
890f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.GROUPS
891f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
892f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_NAME
893f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
894f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_TYPE + "))=0"
895f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN 1"
896f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE 0"
897f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " END)")
898f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_COUNT,
899f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
900f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
901f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
902f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
903f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
904f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
905f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_WITH_PHONES,
906f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
907f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
908f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
909f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + Contacts.HAS_PHONE_NUMBER
910f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
911f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
912f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
913f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
914f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
91582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Contains StatusUpdates columns */
916f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStatusUpdatesProjectionMap = ProjectionMap.builder()
917f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PresenceColumns.RAW_CONTACT_ID)
918f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.DATA_ID, DataColumns.CONCRETE_ID)
919f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_ACCOUNT)
920f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_HANDLE)
921f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PROTOCOL)
922f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // We cannot allow a null in the custom protocol field, because SQLite3 does not
923f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // properly enforce uniqueness of null values
924f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CUSTOM_PROTOCOL,
925f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN " + StatusUpdates.CUSTOM_PROTOCOL + "=''"
926f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN NULL"
927f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE " + StatusUpdates.CUSTOM_PROTOCOL + " END)")
928f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PRESENCE)
929f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CHAT_CAPABILITY)
930f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS)
931f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_TIMESTAMP)
932f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_RES_PACKAGE)
933f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_ICON)
934f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_LABEL)
935f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
936f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
9373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /** Contains StreamItems columns */
9383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final ProjectionMap sStreamItemsProjectionMap = ProjectionMap.builder()
9393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems._ID, StreamItemsColumns.CONCRETE_ID)
9403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(RawContacts.CONTACT_ID)
9413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RAW_CONTACT_ID)
9423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_PACKAGE)
9433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_ICON)
9443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_LABEL)
9453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.TEXT)
9463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.TIMESTAMP)
9473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.COMMENTS)
9483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.ACTION)
9493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.ACTION_URI)
9503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .build();
9513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
9523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final ProjectionMap sStreamItemPhotosProjectionMap = ProjectionMap.builder()
9533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos._ID, StreamItemPhotosColumns.CONCRETE_ID)
9543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RAW_CONTACT_ID)
9553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.STREAM_ITEM_ID)
9563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.SORT_INDEX)
9576802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            .add(StreamItemPhotos.PHOTO_FILE_ID)
9586802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            .add(StreamItemPhotos.PHOTO_URI,
9596802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    "'" + DisplayPhoto.CONTENT_URI + "'||'/'||" + StreamItemPhotos.PHOTO_FILE_ID)
9603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.ACTION, StreamItemPhotosColumns.CONCRETE_ACTION)
9613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.ACTION_URI, StreamItemPhotosColumns.CONCRETE_ACTION_URI)
9621dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.HEIGHT)
9631dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.WIDTH)
9641dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.FILESIZE)
9653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .build();
9663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
9671b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    /** Contains Live Folders columns */
968f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sLiveFoldersProjectionMap = ProjectionMap.builder()
969f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders._ID, Contacts._ID)
970f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders.NAME, Contacts.DISPLAY_NAME)
971f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // TODO: Put contact photo back when we have a way to display a default icon
972f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // for contacts without a photo
973f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // .add(LiveFolders.ICON_BITMAP, Photos.DATA)
974f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
975f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
976d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /** Contains {@link Directory} columns */
977f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDirectoryProjectionMap = ProjectionMap.builder()
978f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory._ID)
979f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.PACKAGE_NAME)
980f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.TYPE_RESOURCE_ID)
981f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DISPLAY_NAME)
982f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DIRECTORY_AUTHORITY)
983f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_TYPE)
984f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_NAME)
985f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.EXPORT_SUPPORT)
986778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.SHORTCUT_SUPPORT)
987778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.PHOTO_SUPPORT)
988f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
9897e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
9909705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    // where clause to update the status_updates table
9919705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private static final String WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE =
9929705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdatesColumns.DATA_ID + " IN (SELECT Distinct " + StatusUpdates.DATA_ID +
9939705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " FROM " + Tables.STATUS_UPDATES + " LEFT OUTER JOIN " + Tables.PRESENCE +
9949705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " ON " + StatusUpdatesColumns.DATA_ID + " = " + StatusUpdates.DATA_ID + " WHERE ";
9959705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
9962526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private static final String[] EMPTY_STRING_ARRAY = new String[0];
9972526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
998bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
999bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Notification ID for failure to import contacts.
1000bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1001bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private static final int LEGACY_IMPORT_FAILED_NOTIFICATION = 1;
100251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
100303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_START_MATCH = "[";
100403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_END_MATCH = "]";
100503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_ELLIPSIS = "...";
100603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final int DEFAULT_SNIPPET_ARG_MAX_TOKENS = -10;
100703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
10089a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    private boolean sIsPhoneInitialized;
10099a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    private boolean sIsPhone;
10109a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
1011f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private StringBuilder mSb = new StringBuilder();
10121129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs1 = new String[1];
10131129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs2 = new String[2];
10142526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private ArrayList<String> mSelectionArgs = Lists.newArrayList();
10152526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
1016f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private Account mAccount;
1017f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
101846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
101946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Stores mapping from type Strings exposed via {@link DataUsageFeedback} to
102046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * type integers in {@link DataUsageStatColumns}.
102146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
102246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final Map<String, Integer> sDataUsageTypeMap;
102346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
10244f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
10254f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
1026a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
1027d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
1028d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
1029a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_ID_DATA);
1030a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/entities", CONTACTS_ID_ENTITIES);
10313653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
10323653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
10332d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*",
10342d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                AGGREGATION_SUGGESTIONS);
1035a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_ID_PHOTO);
1036f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/display_photo",
1037f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_ID_DISPLAY_PHOTO);
10383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/stream_items",
10393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_ID_STREAM_ITEMS);
1040c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter", CONTACTS_FILTER);
10415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER);
10425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP);
10432149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/data", CONTACTS_LOOKUP_DATA);
10445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
10452149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/data",
10462149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                CONTACTS_LOOKUP_ID_DATA);
1047f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/display_photo",
1048f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_LOOKUP_DISPLAY_PHOTO);
1049f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/display_photo",
1050f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_LOOKUP_ID_DISPLAY_PHOTO);
1051a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/entities",
1052a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ENTITIES);
1053a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/entities",
1054a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ID_ENTITIES);
10553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/stream_items",
10563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_LOOKUP_STREAM_ITEMS);
10573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/stream_items",
10583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_LOOKUP_ID_STREAM_ITEMS);
1059f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_vcard/*", CONTACTS_AS_VCARD);
106042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_multi_vcard/*",
106142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                CONTACTS_AS_MULTI_VCARD);
10625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT);
1063ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*",
1064ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                CONTACTS_STREQUENT_FILTER);
10655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP);
106645ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa        matcher.addURI(ContactsContract.AUTHORITY, "contacts/frequent", CONTACTS_FREQUENT);
10673653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
10685ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
10695ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
10705ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA);
1071f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/display_photo",
1072f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                RAW_CONTACTS_ID_DISPLAY_PHOTO);
107346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ENTITY_ID);
10743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/stream_items",
10753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                RAW_CONTACTS_ID_STREAM_ITEMS);
107646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
107746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contact_entities", RAW_CONTACT_ENTITIES);
1078b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
10794f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
10804f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
1081ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
108248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID);
10835e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER);
1084ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
10854a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS);
108648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID);
10871dac83b8fa58944acfd00f44e717a7dddc659d2dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup", EMAILS_LOOKUP);
10885e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP);
10895e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER);
10904a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
1091ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
109248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
109346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        /** "*" is in CSV form with data ids ("123,456,789") */
109446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        matcher.addURI(ContactsContract.AUTHORITY, "data/usagefeedback/*", DATA_USAGE_FEEDBACK_ID);
10951f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1096ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
1097ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
1098ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
1099ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
110035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
1101b5a4add17815167d20a90645779df34cdf45280dFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#",
1102b5a4add17815167d20a90645779df34cdf45280dFred Quintana                SYNCSTATE_ID);
110335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1104a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
1105b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
1106b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
1107b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
1108b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
11094f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1110eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS);
1111eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
111282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates", STATUS_UPDATES);
111382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates/#", STATUS_UPDATES_ID);
11141f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1115c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
1116c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
1117c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
1118c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
11192d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*",
1120c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SHORTCUT);
1121c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
11221b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts",
11231b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS);
11241b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts/*",
11251b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_GROUP_NAME);
11261b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts_with_phones",
11271b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_WITH_PHONES);
11281b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/favorites",
11291b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_FAVORITES);
113009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
113109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "provider_status", PROVIDER_STATUS);
1132d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
1133d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories", DIRECTORIES);
1134d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories/#", DIRECTORIES_ID);
11357a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
11367a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "complete_name", COMPLETE_NAME);
113724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
113824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile", PROFILE);
113924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/entities", PROFILE_ENTITIES);
114024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/data", PROFILE_DATA);
114124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/data/#", PROFILE_DATA_ID);
114224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/as_vcard", PROFILE_AS_VCARD);
114324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts", PROFILE_RAW_CONTACTS);
114424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#",
114524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID);
114624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/data",
114724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID_DATA);
114824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/entity",
114924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID_ENTITIES);
115046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
11513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items", STREAM_ITEMS);
11523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/photo", STREAM_ITEMS_PHOTOS);
11533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#", STREAM_ITEMS_ID);
11543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#/photo", STREAM_ITEMS_ID_PHOTOS);
11553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#/photo/#",
11563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                STREAM_ITEMS_ID_PHOTOS_ID);
11573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items_limit", STREAM_ITEMS_LIMIT);
11583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
1159f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "display_photo/*", DISPLAY_PHOTO);
1160f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "photo_dimensions", PHOTO_DIMENSIONS);
1161f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
116246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        HashMap<String, Integer> tmpTypeMap = new HashMap<String, Integer>();
116346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_CALL, DataUsageStatColumns.USAGE_TYPE_INT_CALL);
116446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_LONG_TEXT,
116546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT);
116646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_SHORT_TEXT,
116746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DataUsageStatColumns.USAGE_TYPE_INT_SHORT_TEXT);
116846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        sDataUsageTypeMap = Collections.unmodifiableMap(tmpTypeMap);
116919a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    }
117019a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
1171d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static class DirectoryInfo {
1172d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String authority;
1173d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountName;
1174d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountType;
1175d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
1176d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
1177d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
1178d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Cached information about contact directories.
1179d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
11804458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private HashMap<String, DirectoryInfo> mDirectoryCache = new HashMap<String, DirectoryInfo>();
11814458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private boolean mDirectoryCacheValid = false;
1182d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
11833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
1184ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * An entry in group id cache. It maps the combination of (account type, account name
1185ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * and source id) to group row id.
1186ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     */
1187e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    public static class GroupIdCacheEntry {
1188ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType;
1189ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName;
1190ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String sourceId;
1191ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        long groupId;
1192ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    }
1193a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1194e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // We don't need a soft cache for groups - the assumption is that there will only
1195e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // be a small number of contact groups. The cache is keyed off source id.  The value
1196e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // is a list of groups with this group id.
1197e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    private HashMap<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache = Maps.newHashMap();
1198e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov
119924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
120024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Cached information about the contact ID and raw contact IDs that make up the user's
120124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * profile entry.
120224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
120324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static class ProfileIdCache {
120424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        boolean inited;
120524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        long profileContactId;
120624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        Set<Long> profileRawContactIds = Sets.newHashSet();
120724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        Set<Long> profileDataIds = Sets.newHashSet();
120824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
120924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        /**
121024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         * Initializes the cache of profile contact and raw contact IDs.  Does nothing if
121124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         * the cache is already initialized (unless forceRefresh is set to true).
121224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         * @param db The contacts database.
121324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         * @param forceRefresh Whether to force re-initialization of the cache.
121424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         */
121524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        private void init(SQLiteDatabase db, boolean forceRefresh) {
121624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            if (!inited || forceRefresh) {
121724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                profileContactId = 0;
121824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                profileRawContactIds.clear();
121924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                profileDataIds.clear();
122024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                Cursor c = db.rawQuery("SELECT " +
122124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        RawContactsColumns.CONCRETE_CONTACT_ID + "," +
122224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        RawContactsColumns.CONCRETE_ID + "," +
122324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        DataColumns.CONCRETE_ID +
122424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        " FROM " + Tables.RAW_CONTACTS + " JOIN " + Tables.ACCOUNTS + " ON " +
122524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        RawContactsColumns.CONCRETE_ID + "=" +
122624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        AccountsColumns.PROFILE_RAW_CONTACT_ID +
122724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        " JOIN " + Tables.DATA + " ON " +
122824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        RawContactsColumns.CONCRETE_ID + "=" + DataColumns.CONCRETE_RAW_CONTACT_ID,
122924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        null);
123024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                try {
123124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    while (c.moveToNext()) {
123224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        if (profileContactId == 0) {
123324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                            profileContactId = c.getLong(0);
123424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        }
123524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        profileRawContactIds.add(c.getLong(1));
123624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        profileDataIds.add(c.getLong(2));
123724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    }
123824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                } finally {
123924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    c.close();
124024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                }
124124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
124224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
124324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
124424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
124524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private ProfileIdCache mProfileIdCache;
124624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1247f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
1248f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Maximum dimension (height or width) of display photos.  Larger images will be scaled
1249f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * to fit.
1250f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
1251f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private int mMaxDisplayPhotoDim;
1252f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1253f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
1254f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Maximum dimension (height or width) of photo thumbnails.
1255f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
1256f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private int mMaxThumbnailPhotoDim;
1257f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
12583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private HashMap<String, DataRowHandler> mDataRowHandlers;
1259b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private ContactsDatabaseHelper mDbHelper;
126031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
1261f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private PhotoStore mPhotoStore;
1262f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
12634097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
1264f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private NameLookupBuilder mNameLookupBuilder;
1265315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
1266622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private PostalSplitter mPostalSplitter;
1267622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
126872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    private ContactDirectoryManager mContactDirectoryManager;
1269622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private ContactAggregator mContactAggregator;
1270f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
1271a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    private GlobalSearchSupport mGlobalSearchSupport;
1272d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov    private CommonNicknameCache mCommonNicknameCache;
1273f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    private SearchIndexManager mSearchIndexManager;
1274a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
127520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private ContentValues mValues = new ContentValues();
127673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    private HashMap<String, Boolean> mAccountWritability = Maps.newHashMap();
127720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
127809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private int mProviderStatus = ProviderStatus.STATUS_NORMAL;
12793826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private boolean mProviderStatusUpdateNeeded;
128009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private long mEstimatedStorageRequirement = 0;
128115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private volatile CountDownLatch mReadAccessLatch;
128215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private volatile CountDownLatch mWriteAccessLatch;
128315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private boolean mAccountUpdateListenerRegistered;
1284bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private boolean mOkToOpenAccess = true;
128573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
1286d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov    private TransactionContext mTransactionContext = new TransactionContext();
1287de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov
12881a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey    private boolean mVisibleTouched = false;
12891a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
129081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    private boolean mSyncToNetwork;
129181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
12924cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    private Locale mCurrentLocale;
12933826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private int mContactsAccountCount;
1294d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov
1295bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private HandlerThread mBackgroundThread;
1296bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private Handler mBackgroundHandler;
1297bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1298f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private long mLastPhotoCleanup = 0;
1299f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
13004f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
13014f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
1302de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        super.onCreate();
1303ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        try {
1304ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return initialize();
1305ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        } catch (RuntimeException e) {
1306ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            Log.e(TAG, "Cannot start provider", e);
1307ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return false;
1308ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        }
1309ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    }
131035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1311ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    private boolean initialize() {
131215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        StrictMode.setThreadPolicy(
131315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
131415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
13153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Resources resources = getContext().getResources();
1316f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mMaxDisplayPhotoDim = resources.getInteger(
1317f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                R.integer.config_max_display_photo_dim);
1318f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mMaxThumbnailPhotoDim = resources.getInteger(
1319f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                R.integer.config_max_thumbnail_photo_dim);
13203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
132124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        mProfileIdCache = new ProfileIdCache();
1322b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper = (ContactsDatabaseHelper)getDatabaseHelper();
132372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        mContactDirectoryManager = new ContactDirectoryManager(this);
1324a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mGlobalSearchSupport = new GlobalSearchSupport(this);
1325f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mPhotoStore = new PhotoStore(getContext().getFilesDir(), mDbHelper);
132665ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov
1327bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        // The provider is closed for business until fully initialized
132815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mReadAccessLatch = new CountDownLatch(1);
132915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mWriteAccessLatch = new CountDownLatch(1);
133072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
1331bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundThread = new HandlerThread("ContactsProviderWorker",
1332bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                Process.THREAD_PRIORITY_BACKGROUND);
1333bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundThread.start();
1334bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler = new Handler(mBackgroundThread.getLooper()) {
1335bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            @Override
1336bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            public void handleMessage(Message msg) {
1337bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                performBackgroundTask(msg.what, msg.obj);
1338bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1339bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        };
13402a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov
134115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_INITIALIZE);
1342bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS);
1343bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
1344bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_LOCALE);
1345bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM);
134605e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_SEARCH_INDEX);
1347bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_PROVIDER_STATUS);
134815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_OPEN_WRITE_ACCESS);
1349f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
13503826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
135149d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov        return true;
13524f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
13534f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1354767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    /**
135551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * (Re)allocates all locale-sensitive structures.
135651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
135704b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov    private void initForDefaultLocale() {
135815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        Context context = getContext();
135915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mLegacyApiSupport = new LegacyApiSupport(context, mDbHelper, this, mGlobalSearchSupport);
13604cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mCurrentLocale = getLocale();
136104b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov        mNameSplitter = mDbHelper.createNameSplitter();
13624cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
13634cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mPostalSplitter = new PostalSplitter(mCurrentLocale);
136451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        mCommonNicknameCache = new CommonNicknameCache(mDbHelper.getReadableDatabase());
1365cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao        ContactLocaleUtils.getIntance().setLocale(mCurrentLocale);
13665b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator = new ContactAggregator(this, mDbHelper,
136715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                createPhotoPriorityResolver(context), mNameSplitter, mCommonNicknameCache);
13685b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
1369f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov        mSearchIndexManager = new SearchIndexManager(this);
13705b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
1371bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers = new HashMap<String, DataRowHandler>();
1372bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1373bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Email.CONTENT_ITEM_TYPE,
13746d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForEmail(context, mDbHelper, mContactAggregator));
1375bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Im.CONTENT_ITEM_TYPE,
13766d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForIm(context, mDbHelper, mContactAggregator));
1377bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Organization.CONTENT_ITEM_TYPE,
13786d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForOrganization(context, mDbHelper, mContactAggregator));
1379bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Phone.CONTENT_ITEM_TYPE,
13806d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForPhoneNumber(context, mDbHelper, mContactAggregator));
1381bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE,
13826d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForNickname(context, mDbHelper, mContactAggregator));
1383bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(StructuredName.CONTENT_ITEM_TYPE,
13846d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForStructuredName(context, mDbHelper, mContactAggregator,
1385bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mNameSplitter, mNameLookupBuilder));
1386bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(StructuredPostal.CONTENT_ITEM_TYPE,
13876d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForStructuredPostal(context, mDbHelper, mContactAggregator,
1388bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mPostalSplitter));
1389bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(GroupMembership.CONTENT_ITEM_TYPE,
13906d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForGroupMembership(context, mDbHelper, mContactAggregator,
1391bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mGroupIdCache));
1392bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Photo.CONTENT_ITEM_TYPE,
1393f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                new DataRowHandlerForPhoto(context, mDbHelper, mContactAggregator, mPhotoStore));
13946d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov        mDataRowHandlers.put(Note.CONTENT_ITEM_TYPE,
13956d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForNote(context, mDbHelper, mContactAggregator));
1396bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1397bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1398bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    /**
1399bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     * Visible for testing.
1400bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     */
1401bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    /* package */ PhotoPriorityResolver createPhotoPriorityResolver(Context context) {
1402bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        return new PhotoPriorityResolver(context);
1403bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1404bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1405bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task) {
1406bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendEmptyMessage(task);
1407bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1408bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1409bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task, Object arg) {
1410bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendMessage(mBackgroundHandler.obtainMessage(task, arg));
1411bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1412bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1413bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void performBackgroundTask(int task, Object arg) {
1414bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        switch (task) {
141515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_INITIALIZE: {
141615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                initForDefaultLocale();
141715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch.countDown();
141815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch = null;
141915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                break;
142015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            }
142115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
142215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_OPEN_WRITE_ACCESS: {
1423bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (mOkToOpenAccess) {
142415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch.countDown();
142515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch = null;
1426bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1427bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1428bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1429bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1430bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS: {
1431bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (isLegacyContactImportNeeded()) {
1432bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    importLegacyContactsInBackground();
1433bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1434bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1435bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1436bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1437bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_ACCOUNTS: {
143815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Context context = getContext();
143915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                if (!mAccountUpdateListenerRegistered) {
144015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    AccountManager.get(context).addOnAccountsUpdatedListener(this, null, false);
144115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mAccountUpdateListenerRegistered = true;
144215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                }
144315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
144415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Account[] accounts = AccountManager.get(context).getAccounts();
1445bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                boolean accountsChanged = updateAccountsInBackground(accounts);
1446bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateContactsAccountCount(accounts);
1447bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateDirectoriesInBackground(accountsChanged);
1448bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1449bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1450bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1451bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_LOCALE: {
1452bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateLocaleInBackground();
1453bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1454bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1455bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1456fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            case BACKGROUND_TASK_CHANGE_LOCALE: {
1457fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                changeLocaleInBackground();
1458fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                break;
1459fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            }
1460fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1461bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM: {
1462bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (isAggregationUpgradeNeeded()) {
1463bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    upgradeAggregationAlgorithmInBackground();
1464bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1465bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1466bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1467bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
146805e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_SEARCH_INDEX: {
146905e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                updateSearchIndexInBackground();
147005e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                break;
147105e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            }
147205e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
1473bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_PROVIDER_STATUS: {
1474bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateProviderStatus();
1475bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1476bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1477bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1478bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_DIRECTORIES: {
1479bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (arg != null) {
1480bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    mContactDirectoryManager.onPackageChanged((String) arg);
1481bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1482bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1483bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1484f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1485f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case BACKGROUND_TASK_CLEANUP_PHOTOS: {
1486f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Check rate limit.
1487f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long now = System.currentTimeMillis();
1488f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (now - mLastPhotoCleanup > PHOTO_CLEANUP_RATE_LIMIT) {
1489f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    mLastPhotoCleanup = now;
1490f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    cleanupPhotoStore();
1491f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    break;
1492f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
1493f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1494bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
14954cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
14964cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao
149753fac8f99f3884c372c907a76766d27fa9e1d95fDmitri Plotnikov    public void onLocaleChanged() {
14983826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
14993826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
15004f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov            return;
15014f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov        }
15024f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov
1503fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_CHANGE_LOCALE);
15044cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
150551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
150651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    /**
150751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * Verifies that the contacts database is properly configured for the current locale.
150851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * If not, changes the database locale to the current locale using an asynchronous task.
150951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * This needs to be done asynchronously because the process involves rebuilding
151051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * large data structures (name lookup, sort keys), which can take minutes on
151151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * a large set of contacts.
151251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
1513bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateLocaleInBackground() {
1514f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
1515f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        // The process is already running - postpone the change
1516f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        if (mProviderStatus == ProviderStatus.STATUS_CHANGING_LOCALE) {
1517f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov            return;
1518f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        }
1519f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
152051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
152151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final String providerLocale = prefs.getString(PREF_LOCALE, null);
152251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final Locale currentLocale = mCurrentLocale;
152351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        if (currentLocale.toString().equals(providerLocale)) {
152451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            return;
152551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        }
152651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
152751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        int providerStatus = mProviderStatus;
152851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_CHANGING_LOCALE);
1529bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDbHelper.setLocale(this, currentLocale);
1530bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        prefs.edit().putString(PREF_LOCALE, currentLocale.toString()).apply();
1531bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        setProviderStatus(providerStatus);
1532bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
153351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
1534fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    /**
1535fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     * Reinitializes the provider for a new locale.
1536fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     */
1537fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    private void changeLocaleInBackground() {
1538fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Re-initializing the provider without stopping it.
1539fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Locking the database will prevent inserts/updates/deletes from
1540fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // running at the same time, but queries may still be running
1541fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // on other threads. Those queries may return inconsistent results.
1542fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        SQLiteDatabase db = mDbHelper.getWritableDatabase();
1543fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        db.beginTransaction();
1544fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        try {
1545fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            initForDefaultLocale();
1546fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.setTransactionSuccessful();
1547fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        } finally {
1548fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.endTransaction();
1549fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        }
1550fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1551fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        updateLocaleInBackground();
1552fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    }
1553fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
155405e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    protected void updateSearchIndexInBackground() {
155505e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        mSearchIndexManager.updateIndex();
155605e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    }
155705e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
1558bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateDirectoriesInBackground(boolean rescan) {
1559bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mContactDirectoryManager.scanAllPackages(rescan);
156051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
156151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
15623826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateProviderStatus() {
15633826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
15643826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
15653826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return;
15663826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
15673826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
15683826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mContactsAccountCount == 0
156949d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                && DatabaseUtils.queryNumEntries(mDbHelper.getReadableDatabase(),
157049d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                        Tables.CONTACTS, null) == 0) {
15713826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            setProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS);
15723826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } else {
15733826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            setProviderStatus(ProviderStatus.STATUS_NORMAL);
15743826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
15753826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
15763826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
157731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
1578f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    protected void cleanupPhotoStore() {
15796802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        SQLiteDatabase db = mDbHelper.getWritableDatabase();
15806802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
15816802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Assemble the set of photo store file IDs that are in use, and send those to the photo
1582f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // store.  Any photos that aren't in that set will be deleted, and any photos that no
1583f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // longer exist in the photo store will be returned for us to clear out in the DB.
15846802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Cursor c = db.query(Views.DATA, new String[]{Data._ID, Photo.PHOTO_FILE_ID},
1585f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                Data.MIMETYPE + "=" + Photo.MIMETYPE + " AND "
1586f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        + Photo.PHOTO_FILE_ID + " IS NOT NULL", null, null, null, null);
15876802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Set<Long> usedPhotoFileIds = Sets.newHashSet();
15886802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> photoFileIdToDataId = Maps.newHashMap();
1589f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        try {
1590f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            while (c.moveToNext()) {
15916802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long dataId = c.getLong(0);
15926802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long photoFileId = c.getLong(1);
15936802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                usedPhotoFileIds.add(photoFileId);
15946802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                photoFileIdToDataId.put(photoFileId, dataId);
15956802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
15966802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        } finally {
15976802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            c.close();
15986802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
15996802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
16006802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Also query for all social stream item photos.
16016802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        c = db.query(Tables.STREAM_ITEM_PHOTOS,
16026802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                new String[]{
16036802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        StreamItemPhotos._ID,
16046802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        StreamItemPhotos.STREAM_ITEM_ID,
16056802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        StreamItemPhotos.PHOTO_FILE_ID
16066802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                },
16076802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                null, null, null, null, null);
16086802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> photoFileIdToStreamItemPhotoId = Maps.newHashMap();
16096802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> streamItemPhotoIdToStreamItemId = Maps.newHashMap();
16106802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        try {
16116802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            while (c.moveToNext()) {
16126802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long streamItemPhotoId = c.getLong(0);
16136802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long streamItemId = c.getLong(1);
16146802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long photoFileId = c.getLong(2);
16156802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                usedPhotoFileIds.add(photoFileId);
16166802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                photoFileIdToStreamItemPhotoId.put(photoFileId, streamItemPhotoId);
16176802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                streamItemPhotoIdToStreamItemId.put(streamItemPhotoId, streamItemId);
1618f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1619f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } finally {
1620f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            c.close();
1621f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
1622f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1623f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // Run the photo store cleanup.
16246802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Set<Long> missingPhotoIds = mPhotoStore.cleanup(usedPhotoFileIds);
1625f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1626f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // If any of the keys we're using no longer exist, clean them up.
16276802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (!missingPhotoIds.isEmpty()) {
1628f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            ArrayList<ContentProviderOperation> ops = Lists.newArrayList();
16296802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            for (long missingPhotoId : missingPhotoIds) {
16306802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (photoFileIdToDataId.containsKey(missingPhotoId)) {
16316802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    long dataId = photoFileIdToDataId.get(missingPhotoId);
1632f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    ContentValues updateValues = new ContentValues();
1633f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    updateValues.putNull(Photo.PHOTO_FILE_ID);
1634f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    ops.add(ContentProviderOperation.newUpdate(
16356802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            ContentUris.withAppendedId(Data.CONTENT_URI, dataId))
1636f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            .withValues(updateValues).build());
1637f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
16386802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (photoFileIdToStreamItemPhotoId.containsKey(missingPhotoId)) {
16396802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // For missing photos that were in stream item photos, just delete the stream
16406802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // item photo.
16416802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    long streamItemPhotoId = photoFileIdToStreamItemPhotoId.get(missingPhotoId);
16426802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    long streamItemId = streamItemPhotoIdToStreamItemId.get(streamItemPhotoId);
16436802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    ops.add(ContentProviderOperation.newDelete(
16446802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            StreamItems.CONTENT_URI.buildUpon()
16456802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    .appendPath(String.valueOf(streamItemId))
16466802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    .appendPath(StreamItems.StreamItemPhotos.CONTENT_DIRECTORY)
16476802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    .appendPath(String.valueOf(streamItemPhotoId))
16486802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    .build()).build());
16496802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                }
1650f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1651f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            try {
1652f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                applyBatch(ops);
1653f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            } catch (OperationApplicationException oae) {
1654f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Not a fatal problem (and we'll try again on the next cleanup).
1655f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                Log.e(TAG, "Failed to clean up outdated photo references", oae);
1656f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1657f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
1658f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
1659f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1660f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /* Visible for testing */
1661de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    @Override
1662b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
1663b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        return ContactsDatabaseHelper.getInstance(context);
166431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
166531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
1666f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /* package */ PhotoStore getPhotoStore() {
1667f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        return mPhotoStore;
1668f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
1669f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
167087614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    /* package */ int getMaxDisplayPhotoDim() {
167187614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro        return mMaxDisplayPhotoDim;
167287614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    }
167387614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro
167487614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    /* package */ int getMaxThumbnailPhotoDim() {
167587614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro        return mMaxThumbnailPhotoDim;
167687614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    }
167787614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro
1678013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    /* package */ NameSplitter getNameSplitter() {
1679013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        return mNameSplitter;
1680013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    }
1681013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov
16825df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    /* package */ NameLookupBuilder getNameLookupBuilder() {
16835df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov        return mNameLookupBuilder;
16845df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    }
16855df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov
16865dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    /* Visible for testing */
1687ed78fd6df5e9f3a2d572162e5d374d1f4a625bddDmitri Plotnikov    public ContactDirectoryManager getContactDirectoryManagerForTest() {
168872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        return mContactDirectoryManager;
168972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
169072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
169172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    /* Visible for testing */
16925dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    protected Locale getLocale() {
16935dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        return Locale.getDefault();
16945dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
16955dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
16963d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    protected boolean isLegacyContactImportNeeded() {
1697b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        int version = Integer.parseInt(mDbHelper.getProperty(PROPERTY_CONTACTS_IMPORTED, "0"));
1698b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        return version < PROPERTY_CONTACTS_IMPORT_VERSION;
16993d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
17003d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1701568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected LegacyContactImporter getLegacyContactImporter() {
1702568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return new LegacyContactImporter(getContext(), this);
1703568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1704568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1705568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
1706bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     * Imports legacy contacts as a background task.
1707568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
1708bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private void importLegacyContactsInBackground() {
1709bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Importing legacy contacts");
1710bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADING);
1711568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1712bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
1713bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDbHelper.setLocale(this, mCurrentLocale);
1714bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        prefs.edit().putString(PREF_LOCALE, mCurrentLocale.toString()).commit();
1715568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1716bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        LegacyContactImporter importer = getLegacyContactImporter();
1717bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        if (importLegacyContacts(importer)) {
1718bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            onLegacyContactImportSuccess();
1719bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        } else {
1720bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            onLegacyContactImportFailure();
1721bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
1722568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1723568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1724bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1725bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Unlocks the provider and declares that the import process is complete.
1726bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1727bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportSuccess() {
1728bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
1729bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)getContext().getSystemService(Context.NOTIFICATION_SERVICE);
1730bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.cancel(LEGACY_IMPORT_FAILED_NOTIFICATION);
1731bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1732b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        // Store a property in the database indicating that the conversion process succeeded
1733b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mDbHelper.setProperty(PROPERTY_CONTACTS_IMPORTED,
1734b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov                String.valueOf(PROPERTY_CONTACTS_IMPORT_VERSION));
1735bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_NORMAL);
1736bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Completed import of legacy contacts");
1737bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    }
1738bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1739bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1740bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Announces the provider status and keeps the provider locked.
1741bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1742bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportFailure() {
1743bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Context context = getContext();
1744bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
1745bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
1746bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1747bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        // Show a notification
1748bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Notification n = new Notification(android.R.drawable.stat_notify_error,
1749bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_ticker),
1750bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                System.currentTimeMillis());
1751bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.setLatestEventInfo(context,
1752bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_title),
1753bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_text),
1754bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                PendingIntent.getActivity(context, 0, new Intent(Intents.UI.LIST_DEFAULT), 0));
1755bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
1756bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1757bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.notify(LEGACY_IMPORT_FAILED_NOTIFICATION, n);
1758bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1759bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADE_OUT_OF_MEMORY);
1760bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Failed to import legacy contacts");
1761bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1762bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        // Do not let any database changes until this issue is resolved.
1763bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mOkToOpenAccess = false;
17643d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
17653d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
17663d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /* Visible for testing */
1767568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
17680e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        boolean aggregatorEnabled = mContactAggregator.isEnabled();
17693d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mContactAggregator.setEnabled(false);
17703d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        try {
1771bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            if (importer.importContacts()) {
1772bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1773bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                // TODO aggregate all newly added raw contacts
1774bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                mContactAggregator.setEnabled(aggregatorEnabled);
1775bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                return true;
1776bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
17773d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } catch (Throwable e) {
17783d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           Log.e(TAG, "Legacy contact import failed", e);
17793d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
1780bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mEstimatedStorageRequirement = importer.getEstimatedStorageRequirement();
1781bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        return false;
17823d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
17833d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1784a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
1785a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
1786a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
1787a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
1788b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper.wipeData();
1789f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mPhotoStore.clear();
17903826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatus = ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS;
1791a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1792a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1793568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
179415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov     * During intialization, this content provider will
1795568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * block all attempts to change contacts data. In particular, it will hold
1796568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * up all contact syncs. As soon as the import process is complete, all
1797568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * processes waiting to write to the provider are unblocked and can proceed
1798568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * to compete for the database transaction monitor.
1799568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
180015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private void waitForAccess(CountDownLatch latch) {
180115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (latch == null) {
180215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            return;
180315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        }
180415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
180515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        while (true) {
180615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            try {
180715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                latch.await();
180815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                return;
180915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            } catch (InterruptedException e) {
181015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Thread.currentThread().interrupt();
1811ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            }
1812568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
1813568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1814568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1815568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1816568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
181715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1818568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.insert(uri, values);
1819568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1820568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1821568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1822568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
182315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (mWriteAccessLatch != null) {
1824bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // We are stuck trying to upgrade contacts db.  The only update request
1825bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // allowed in this case is an update of provider status, which will trigger
1826bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // an attempt to upgrade contacts again.
1827bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            int match = sUriMatcher.match(uri);
1828bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            if (match == PROVIDER_STATUS) {
1829bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                Integer newStatus = values.getAsInteger(ProviderStatus.STATUS);
1830bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                if (newStatus != null && newStatus == ProviderStatus.STATUS_UPGRADING) {
1831bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    scheduleBackgroundTask(BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS);
1832bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 1;
1833bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                } else {
1834bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 0;
1835bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                }
1836bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
1837bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
183815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1839568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.update(uri, values, selection, selectionArgs);
1840568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1841568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1842568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1843568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
184415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1845568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.delete(uri, selection, selectionArgs);
1846568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1847568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1848568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1849568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
1850568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            throws OperationApplicationException {
185115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1852568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.applyBatch(operations);
1853568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1854568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
18554f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
18567b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    public int bulkInsert(Uri uri, ContentValues[] values) {
18577b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov        waitForAccess(mWriteAccessLatch);
18587b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov        return super.bulkInsert(uri, values);
18597b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    }
18607b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov
18617b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    @Override
1862285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void onBeginTransaction() {
1863bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1864b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "onBeginTransaction");
1865b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1866285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.onBeginTransaction();
18671ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.clearPendingAggregations();
1868d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.clear();
1869b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1870b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1871285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1872285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
1873285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void beforeTransactionCommit() {
18741129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
1875bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1876b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "beforeTransactionCommit");
1877b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1878285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.beforeTransactionCommit();
1879b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
1880bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        mContactAggregator.aggregateInTransaction(mTransactionContext, mDb);
18811a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (mVisibleTouched) {
18821a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = false;
1883b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.updateAllVisible();
18841a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        }
18853826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
1886bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        updateSearchIndexInTransaction();
1887bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov
18883826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatusUpdateNeeded) {
18893826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
18903826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatusUpdateNeeded = false;
18913826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
1892b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1893b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1894bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov    private void updateSearchIndexInTransaction() {
1895bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        Set<Long> staleContacts = mTransactionContext.getStaleSearchIndexContactIds();
1896bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        Set<Long> staleRawContacts = mTransactionContext.getStaleSearchIndexRawContactIds();
1897bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        if (!staleContacts.isEmpty() || !staleRawContacts.isEmpty()) {
1898bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mSearchIndexManager.updateIndexForRawContacts(staleContacts, staleRawContacts);
1899bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mTransactionContext.clearSearchIndexUpdates();
1900bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        }
1901bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov    }
1902bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov
1903b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void flushTransactionalChanges() {
1904bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1905b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "flushTransactionChanges");
1906b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
19071129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
190824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // Determine whether we need to refresh the profile ID cache.
190924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        boolean profileCacheRefreshNeeded = false;
191024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1911d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        for (long rawContactId : mTransactionContext.getInsertedRawContactIds()) {
19128ab0b7a48efe540226253567bcf6fdbc487186a2Dmitri Plotnikov            mDbHelper.updateRawContactDisplayName(mDb, rawContactId);
1913bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mContactAggregator.onRawContactInsert(mTransactionContext, mDb, rawContactId);
1914285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        }
1915b5a4add17815167d20a90645779df34cdf45280dFred Quintana
191624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        Map<Long, Account> insertedProfileRawContactAccountMap =
191724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                mTransactionContext.getInsertedProfileRawContactIds();
191824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (!insertedProfileRawContactAccountMap.isEmpty()) {
191924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            for (long profileRawContactId : insertedProfileRawContactAccountMap.keySet()) {
192024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                mDbHelper.updateRawContactDisplayName(mDb, profileRawContactId);
192124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                mContactAggregator.onProfileRawContactInsert(mTransactionContext, mDb,
192224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        profileRawContactId,
192324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        insertedProfileRawContactAccountMap.get(profileRawContactId));
192424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
192524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            profileCacheRefreshNeeded = true;
192624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
192724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1928d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        Set<Long> dirtyRawContacts = mTransactionContext.getDirtyRawContactIds();
1929d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!dirtyRawContacts.isEmpty()) {
1930a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
1931a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_DIRTY_SQL);
1932d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, dirtyRawContacts);
1933a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
1934a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
193524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
193624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            profileCacheRefreshNeeded = profileCacheRefreshNeeded ||
193724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    !Collections.disjoint(mProfileIdCache.profileRawContactIds, dirtyRawContacts);
1938a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        }
1939a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
1940d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        Set<Long> updatedRawContacts = mTransactionContext.getUpdatedRawContactIds();
1941d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!updatedRawContacts.isEmpty()) {
1942a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
1943a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_VERSION_SQL);
1944d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, updatedRawContacts);
1945a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
1946a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
194724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
194824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            profileCacheRefreshNeeded = profileCacheRefreshNeeded ||
194924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    !Collections.disjoint(mProfileIdCache.profileRawContactIds, updatedRawContacts);
1950b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1951b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1952d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        for (Map.Entry<Long, Object> entry : mTransactionContext.getUpdatedSyncStates()) {
1953b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long id = entry.getKey();
19549d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            if (mDbHelper.getSyncState().update(mDb, id, entry.getValue()) <= 0) {
19559d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                throw new IllegalStateException(
19569d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                        "unable to update sync state, does it still exist?");
19579d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            }
1958b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1959b5a4add17815167d20a90645779df34cdf45280dFred Quintana
196024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (profileCacheRefreshNeeded) {
196124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Force the profile ID cache to refresh.
196224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            mProfileIdCache.init(mDb, true);
196324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
196424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1965d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.clear();
1966b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1967b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1968a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /**
1969a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * Appends comma separated ids.
1970a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * @param ids Should not be empty
1971a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     */
1972d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov    private void appendIds(StringBuilder sb, Set<Long> ids) {
1973b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long id : ids) {
1974a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            sb.append(id).append(',');
1975b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1976a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
1977a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        sb.setLength(sb.length() - 1); // Yank the last comma
1978285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
1979285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
198024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
198124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Checks whether the given contact ID represents the user's personal profile - if it is, calls
198224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * a permission check (for writing the profile if forWrite is true, for reading the profile
198324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * otherwise).  If the contact ID is not the user's profile, no check is executed.
1984afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro     * @param db The database.
198524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param contactId The 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 enforceProfilePermissionForContact(SQLiteDatabase db, long contactId,
1989afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro            boolean forWrite) {
1990afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        mProfileIdCache.init(db, false);
199124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (mProfileIdCache.profileContactId == contactId) {
199224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermission(forWrite);
199324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
199424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
199524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
199624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
199724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Checks whether the given raw contact ID is a member of the user's personal profile - if it
199824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * is, calls a permission check (for writing the profile if forWrite is true, for reading the
199924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * profile otherwise).  If the raw contact ID is not in the user's profile, no check is
200024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * executed.
2001afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro     * @param db The database.
200224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param rawContactId The raw contact ID to be checked.
200324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forWrite Whether the caller is attempting to do a write (vs. read) operation.
200424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
2005afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro    private void enforceProfilePermissionForRawContact(SQLiteDatabase db, long rawContactId,
2006afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro            boolean forWrite) {
2007afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        mProfileIdCache.init(db, false);
200824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (mProfileIdCache.profileRawContactIds.contains(rawContactId)) {
200924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermission(forWrite);
201024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
201124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
201224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
201324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
201424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Checks whether the given data ID is a member of the user's personal profile - if it is,
201524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * calls a permission check (for writing the profile if forWrite is true, for reading the
201624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * profile otherwise).  If the data ID is not in the user's profile, no check is executed.
2017afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro     * @param db The database.
201824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param dataId The data ID to be checked.
201924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forWrite Whether the caller is attempting to do a write (vs. read) operation.
202024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
2021afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro    private void enforceProfilePermissionForData(SQLiteDatabase db, long dataId, boolean forWrite) {
2022afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        mProfileIdCache.init(db, false);
202324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (mProfileIdCache.profileDataIds.contains(dataId)) {
202424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermission(forWrite);
202524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
202624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
202724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
202824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
202924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Performs a permission check for WRITE_PROFILE or READ_PROFILE (depending on the parameter).
203024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * If the permission check fails, this will throw a SecurityException.
203124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forWrite Whether the caller is attempting to do a write (vs. read) operation.
203224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
203324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private void enforceProfilePermission(boolean forWrite) {
203424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        String profilePermission = forWrite
203524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                ? "android.permission.WRITE_PROFILE"
203624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                : "android.permission.READ_PROFILE";
203724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        getContext().enforceCallingOrSelfPermission(profilePermission, null);
203824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
203924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2040285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2041cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    protected void notifyChange() {
204281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        notifyChange(mSyncToNetwork);
204381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = false;
204481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
204581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
204681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    protected void notifyChange(boolean syncToNetwork) {
204781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
204881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                syncToNetwork);
2049cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    }
2050568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
205151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    protected void setProviderStatus(int status) {
20523826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != status) {
20533826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatus = status;
20543826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            getContext().getContentResolver().notifyChange(ProviderStatus.CONTENT_URI, null, false);
20553826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
205651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
205751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
2058f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    public DataRowHandler getDataRowHandler(final String mimeType) {
20593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
20603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
20616d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov            handler = new DataRowHandlerForCustomMimetype(
20626d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                    getContext(), mDbHelper, mContactAggregator, mimeType);
20633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
20643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
20653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
20663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
20673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
20684f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2069de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected Uri insertInTransaction(Uri uri, ContentValues values) {
2070bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
20711129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Log.v(TAG, "insertInTransaction: " + uri + " " + values);
2072b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2073f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2074f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2075f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2076f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2077a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
2078a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
207935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2080a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
208135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2082b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                id = mDbHelper.getSyncState().insert(mDb, values);
208335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
208435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2085d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
2086d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                insertContact(values);
20876bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
20886bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
20896bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
209024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
209124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                throw new UnsupportedOperationException(
209224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        "The profile contact is created automatically");
209324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
209424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
20955ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
209624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                id = insertRawContact(uri, values, callerIsSyncAdapter, false);
2097f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2098a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2099a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2100a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
21015ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
21025ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(1));
2103f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2104f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2105a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2106a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2107a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
21083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case RAW_CONTACTS_ID_STREAM_ITEMS: {
21093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                values.put(StreamItems.RAW_CONTACT_ID, uri.getPathSegments().get(1));
21103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItem(uri, values);
21113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
21123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
21133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
21143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
211524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS: {
211624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(true);
211724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                id = insertRawContact(uri, values, callerIsSyncAdapter, true);
211824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                mSyncToNetwork |= !callerIsSyncAdapter;
211924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
212024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
212124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2122a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case DATA: {
2123f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2124f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2125a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2126a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2127a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2128ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
2129f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                id = insertGroup(uri, values, callerIsSyncAdapter);
2130f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2131ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2132ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2133ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2134eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
21355aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertSettings(uri, values);
213643880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2137eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
2138eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2139eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
214082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
214182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                id = insertStatusUpdate(values);
21421f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
21431f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
21441f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
21453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
21463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItem(uri, values);
21473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
21483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
21493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
21503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
21513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
21523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItemPhoto(uri, values);
21533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
21543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
21553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
21563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
21573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
21583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                values.put(StreamItemPhotos.STREAM_ITEM_ID, uri.getPathSegments().get(1));
21593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItemPhoto(uri, values);
21603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
21613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
21623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
21633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
2164a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
216581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
2166f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
2167a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2168a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
21697e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
21707e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
21717e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
21727e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2173de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        return ContentUris.withAppendedId(uri, id);
2174a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2175a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2176a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2177e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * If account is non-null then store it in the values. If the account is
2178e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * already specified in the values then it must be consistent with the
2179e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * account, if it is non-null.
2180e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *
2181e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param uri Current {@link Uri} being operated on.
2182e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param values {@link ContentValues} to read and possibly update.
2183e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when only one of
2184e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_NAME} or
2185e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_TYPE} is specified, leaving the
2186e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             other undefined.
2187e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when {@link RawContacts#ACCOUNT_NAME}
2188e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             and {@link RawContacts#ACCOUNT_TYPE} are inconsistent between
2189e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             the given {@link Uri} and {@link ContentValues}.
21907e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
2191e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey    private Account resolveAccount(Uri uri, ContentValues values) throws IllegalArgumentException {
2192f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
2193f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
2194e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
2195f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2196f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
2197f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
2198e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialValues = TextUtils.isEmpty(valueAccountName)
2199e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                ^ TextUtils.isEmpty(valueAccountType);
2200e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2201e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri || partialValues) {
2202e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
2203fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
2204fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
2205e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
2206e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2207e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
2208e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
2209e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validUri = !TextUtils.isEmpty(accountName);
2210e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validValues = !TextUtils.isEmpty(valueAccountName);
2211e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2212e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validValues && validUri) {
2213e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Check that accounts match when both present
2214e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            final boolean accountMatch = TextUtils.equals(accountName, valueAccountName)
2215e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    && TextUtils.equals(accountType, valueAccountType);
2216e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            if (!accountMatch) {
2217fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new IllegalArgumentException(mDbHelper.exceptionMessage(
2218fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        "When both specified, ACCOUNT_NAME and ACCOUNT_TYPE must match", uri));
2219e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            }
2220e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validUri) {
2221e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Fill values from Uri when not present
2222f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_NAME, accountName);
2223f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_TYPE, accountType);
2224e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validValues) {
2225f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountName = valueAccountName;
2226f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountType = valueAccountType;
2227e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else {
2228e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            return null;
2229f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
2230f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2231e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Use cached Account object when matches, otherwise create
2232f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mAccount == null
2233f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.name.equals(accountName)
2234f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.type.equals(accountType)) {
2235f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mAccount = new Account(accountName, accountType);
2236035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
2237f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2238e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        return mAccount;
22397e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
22407e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
22417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
2242d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Inserts an item in the contacts table
22436bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
22446bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
22456bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
22466bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
2247d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertContact(ContentValues values) {
2248de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregate contacts are created automatically");
22496bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
22506bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
22516bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
225224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Inserts an item in the raw contacts table
2253a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2254f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param uri the values for the new row
2255f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param values the account this contact should be associated with. may be null.
2256dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana     * @param callerIsSyncAdapter
225724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forProfile Whether this raw contact is being inserted into the user's profile.
2258a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2259a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
226024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private long insertRawContact(Uri uri, ContentValues values, boolean callerIsSyncAdapter,
226124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            boolean forProfile) {
2262f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2263f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2264f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2265f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2266e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final Account account = resolveAccount(uri, mValues);
22677e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
22683d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
22693d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
2270f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
22713d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
22723d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2273f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long rawContactId = mDb.insert(Tables.RAW_CONTACTS, RawContacts.CONTACT_ID, mValues);
2274f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        int aggregationMode = RawContacts.AGGREGATION_MODE_DEFAULT;
227524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (forProfile) {
227624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Profile raw contacts should never be aggregated by the aggregator; they are always
227724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // aggregated under a single profile contact.
227824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            aggregationMode = RawContacts.AGGREGATION_MODE_DISABLED;
227924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        } else if (mValues.containsKey(RawContacts.AGGREGATION_MODE)) {
2280f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            aggregationMode = mValues.getAsInteger(RawContacts.AGGREGATION_MODE);
2281f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        }
2282f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        mContactAggregator.markNewForAggregation(rawContactId, aggregationMode);
2283285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
228424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (forProfile) {
228524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Trigger creation of the user profile Contact (or association with the existing one)
228624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // at the end of the transaction.
228724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            mTransactionContext.profileRawContactInserted(rawContactId, account);
228824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        } else {
228924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Trigger creation of a Contact based on this RawContact at the end of transaction
229024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            mTransactionContext.rawContactInserted(rawContactId, account);
229124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
2292f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2293dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter) {
2294dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            addAutoAddMembership(rawContactId);
2295dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            final Long starred = values.getAsLong(RawContacts.STARRED);
2296dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (starred != null && starred != 0) {
2297dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateFavoritesMembership(rawContactId, starred != 0);
2298dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2299dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2300dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
23013826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
2302023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        return rawContactId;
2303a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2304a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2305dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void addAutoAddMembership(long rawContactId) {
2306dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID,
2307dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2308dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2309dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            insertDataGroupMembership(rawContactId, groupId);
2310dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2311dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2312dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2313dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private Long findGroupByRawContactId(String selection, long rawContactId) {
2314dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        Cursor c = mDb.query(Tables.GROUPS + "," + Tables.RAW_CONTACTS, PROJECTION_GROUP_ID,
2315dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection,
2316dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                new String[]{Long.toString(rawContactId)},
2317dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                null /* groupBy */, null /* having */, null /* orderBy */);
2318dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        try {
2319dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            while (c.moveToNext()) {
2320dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return c.getLong(0);
2321dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2322dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return null;
2323dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        } finally {
2324dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            c.close();
2325dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2326dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2327dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2328dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void updateFavoritesMembership(long rawContactId, boolean isStarred) {
2329dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID,
2330dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2331dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2332dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (isStarred) {
2333dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                insertDataGroupMembership(rawContactId, groupId);
2334dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
2335dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                deleteDataGroupMembership(rawContactId, groupId);
2336dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2337dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2338dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2339dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2340dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void insertDataGroupMembership(long rawContactId, long groupId) {
2341dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        ContentValues groupMembershipValues = new ContentValues();
2342dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.GROUP_ROW_ID, groupId);
2343dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.RAW_CONTACT_ID, rawContactId);
2344dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(DataColumns.MIMETYPE_ID,
2345dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                mDbHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
2346dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mDb.insert(Tables.DATA, null, groupMembershipValues);
2347dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2348dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2349dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void deleteDataGroupMembership(long rawContactId, long groupId) {
2350dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final String[] selectionArgs = {
2351dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(mDbHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE)),
2352dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(groupId),
2353dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(rawContactId)};
2354dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mDb.delete(Tables.DATA, SELECTION_GROUPMEMBERSHIP_DATA, selectionArgs);
2355dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2356dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2357a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2358a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
2359a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2360a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
2361a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2362a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2363f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
2364a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
2365de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.clear();
2366de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.putAll(values);
236767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
2368de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
236920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
237024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // If the data being inserted belongs to the user's profile entry, check for the
237124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // WRITE_PROFILE permission before proceeding.
2372afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        enforceProfilePermissionForRawContact(mDb, rawContactId, true);
237324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2374de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace package with internal mapping
2375de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
2376de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (packageName != null) {
2377b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
2378de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
2379de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.RES_PACKAGE);
2380508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2381de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace mimetype with internal mapping
2382de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String mimeType = mValues.getAsString(Data.MIMETYPE);
2383de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (TextUtils.isEmpty(mimeType)) {
2384de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
2385de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
23864097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
2387b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.getMimeTypeId(mimeType));
2388de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
2389a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2390a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
2391d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        id = rowHandler.insert(mDb, mTransactionContext, rawContactId, mValues);
2392f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2393d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            mTransactionContext.markRawContactDirty(rawContactId);
2394a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2395d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.rawContactUpdated(rawContactId);
2396a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
23974f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
23984f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
23993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
24003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Inserts an item in the stream_items table.  The account is checked against the
24013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account in the raw contact for which the stream item is being inserted.  If the
24023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * new stream item results in more stream items under this raw contact than the limit,
24033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the oldest one will be deleted (note that if the stream item inserted was the
24043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * oldest, it will be immediately deleted, and this will return 0).
24053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
24063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param uri the insertion URI
24073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param values the values for the new row
24083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return the stream item _ID of the newly created row, or 0 if it was not created
24093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
24103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long insertStreamItem(Uri uri, ContentValues values) {
24113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long id = 0;
24123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.clear();
24133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.putAll(values);
24143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long rawContactId = mValues.getAsLong(StreamItems.RAW_CONTACT_ID);
24163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If the data being inserted belongs to the user's profile entry, check for the
24183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // WRITE_PROFILE permission before proceeding.
2419afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        enforceProfilePermissionForRawContact(mDb, rawContactId, true);
24203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Ensure that the raw contact exists and belongs to the caller's account.
24223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, mValues);
24233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccount(account, rawContactId);
24243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24256802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to insert accounts params - they don't exist in the stream items table.
24266802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        mValues.remove(RawContacts.ACCOUNT_NAME);
24276802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        mValues.remove(RawContacts.ACCOUNT_TYPE);
24286802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
24293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Insert the new stream item.
24306802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        id = mDb.insert(Tables.STREAM_ITEMS, null, mValues);
24316802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (id == -1) {
24326802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Insertion failed.
24336802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return 0;
24346802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
24353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check to see if we're over the limit for stream items under this raw contact.
24373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // It's possible that the inserted stream item is older than the the existing
24383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // ones, in which case it may be deleted immediately (resetting the ID to 0).
24393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        id = cleanUpOldStreamItems(rawContactId, id);
24403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return id;
24423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
24433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
24453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Inserts an item in the stream_item_photos table.  The account is checked against
24463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the account in the raw contact that owns the stream item being modified.
24473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
24483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param uri the insertion URI
24493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param values the values for the new row
24506802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @return the stream item photo _ID of the newly created row, or 0 if there was an issue
24516802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     *     with processing the photo or creating the row
24523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
24533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long insertStreamItemPhoto(Uri uri, ContentValues values) {
24543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long id = 0;
24553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.clear();
24563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.putAll(values);
24573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long streamItemId = mValues.getAsLong(StreamItemPhotos.STREAM_ITEM_ID);
24593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        if (streamItemId != 0) {
24603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            long rawContactId = lookupRawContactIdForStreamId(streamItemId);
24613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            // If the data being inserted belongs to the user's profile entry, check for the
24633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            // WRITE_PROFILE permission before proceeding.
2464afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro            enforceProfilePermissionForRawContact(mDb, rawContactId, true);
24653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            // Ensure that the raw contact exists and belongs to the caller's account.
24673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            Account account = resolveAccount(uri, mValues);
24683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            enforceModifyingAccount(account, rawContactId);
24693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24706802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Don't attempt to insert accounts params - they don't exist in the stream item
24716802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // photos table.
24726802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            mValues.remove(RawContacts.ACCOUNT_NAME);
24736802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            mValues.remove(RawContacts.ACCOUNT_TYPE);
24743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24756802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Process the photo and store it.
24766802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            if (processStreamItemPhoto(mValues, false)) {
24776802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // Insert the stream item photo.
24786802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                id = mDb.insert(Tables.STREAM_ITEM_PHOTOS, null, mValues);
24796802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
24803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
24813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return id;
24823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
24833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
24856802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * Processes the photo contained in the {@link ContactsContract.StreamItemPhotos#PHOTO}
24866802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * field of the given values, attempting to store it in the photo store.  If successful,
24876802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * the resulting photo file ID will be added to the values for insert/update in the table.
24886802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * <p>
24896802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * If updating, it is valid for the picture to be empty or unspecified (the function will
24906802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * still return true).  If inserting, a valid picture must be specified.
24916802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @param values The content values provided by the caller.
24926802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @param forUpdate Whether this photo is being processed for update (vs. insert).
24936802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @return Whether the insert or update should proceed.
24946802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     */
24956802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    private boolean processStreamItemPhoto(ContentValues values, boolean forUpdate) {
24966802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (!values.containsKey(StreamItemPhotos.PHOTO)) {
24976802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return forUpdate;
24986802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
24996802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        byte[] photoBytes = values.getAsByteArray(StreamItemPhotos.PHOTO);
25006802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (photoBytes == null) {
25016802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return forUpdate;
25026802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
25036802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
25046802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Process the photo and store it.
25056802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        try {
25066802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            long photoFileId = mPhotoStore.insert(new PhotoProcessor(photoBytes,
25071dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                    mMaxDisplayPhotoDim, mMaxThumbnailPhotoDim, true), true);
25086802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            if (photoFileId != 0) {
25096802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                values.put(StreamItemPhotos.PHOTO_FILE_ID, photoFileId);
25106802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                values.remove(StreamItemPhotos.PHOTO);
25116802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                return true;
25126802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            } else {
25136802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // Couldn't store the photo, return 0.
25146802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                Log.e(TAG, "Could not process stream item photo for insert");
25156802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                return false;
25166802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
25176802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        } catch (IOException ioe) {
25186802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            Log.e(TAG, "Could not process stream item photo for insert", ioe);
25196802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return false;
25206802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
25216802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    }
25226802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
25236802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    /**
25243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Looks up the raw contact ID that owns the specified stream item.
25253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param streamItemId The ID of the stream item.
25263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The associated raw contact ID, or -1 if no such stream item exists.
25273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
25283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long lookupRawContactIdForStreamId(long streamItemId) {
25293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long rawContactId = -1;
25303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c = mDb.query(Tables.STREAM_ITEMS, new String[]{StreamItems.RAW_CONTACT_ID},
25313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                StreamItems._ID + "=?", new String[]{String.valueOf(streamItemId)},
25323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                null, null, null);
25333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
25343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if (c.moveToFirst()) {
25353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                rawContactId = c.getLong(0);
25363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
25373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
25383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
25393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
25403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return rawContactId;
25413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
25423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
25433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
25443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given raw contact ID is owned by the given account.
25453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * If the resolved account is null, this will return true iff the raw contact
25463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * is also associated with the "null" account.
25473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
25483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * If the resolved account does not match, this will throw a security exception.
25493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
25503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param rawContactId The raw contact ID to check for.
25513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
25523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void enforceModifyingAccount(Account account, long rawContactId) {
25533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        String accountSelection = RawContactsColumns.CONCRETE_ID + "=? AND "
25543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_NAME + "=? AND "
25553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + "=?";
25563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        String noAccountSelection = RawContactsColumns.CONCRETE_ID + "=? AND "
25573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " IS NULL AND "
25583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " IS NULL";
25593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c;
25603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        if (account != null) {
25613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContactsColumns.CONCRETE_ID},
25623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    accountSelection,
25633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    new String[]{String.valueOf(rawContactId), mAccount.name, mAccount.type},
25643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    null, null, null);
25653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } else {
25663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContactsColumns.CONCRETE_ID},
25673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    noAccountSelection, new String[]{String.valueOf(rawContactId)},
25683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    null, null, null);
25693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
25703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
25713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if(c.getCount() == 0) {
25723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                throw new SecurityException("Caller account does not match raw contact ID "
25733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    + rawContactId);
25743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
25753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
25763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
25773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
25783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
25793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
25803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
25813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given selection of stream items matches up with the given
25823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account.  If any of the raw contacts fail the account check, this will throw a
25833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * security exception.
25843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
25853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selection The selection.
25863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selectionArgs The selection arguments.
25873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The list of stream item IDs that would be included in this selection.
25883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
25893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private List<Long> enforceModifyingAccountForStreamItems(Account account, String selection,
25903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
25913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemIds = Lists.newArrayList();
25923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
25933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        setTablesAndProjectionMapForStreamItems(qb);
25943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c = qb.query(mDb,
25953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{StreamItems._ID, StreamItems.RAW_CONTACT_ID},
25963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selection, selectionArgs, null, null, null);
25973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
25983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            while (c.moveToNext()) {
25993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                streamItemIds.add(c.getLong(0));
26003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
26013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Throw a security exception if the account doesn't match the raw contact's.
26023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                enforceModifyingAccount(account, c.getLong(1));
26033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
26043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
26053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
26063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
26073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemIds;
26083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
26093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
26103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
26113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given selection of stream item photos matches up with the given
26123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account.  If any of the raw contacts fail the account check, this will throw a
26133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * security exception.
26143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
26153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selection The selection.
26163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selectionArgs The selection arguments.
26173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The list of stream item photo IDs that would be included in this selection.
26183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
26193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private List<Long> enforceModifyingAccountForStreamItemPhotos(Account account, String selection,
26203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
26213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemPhotoIds = Lists.newArrayList();
26223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
26233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        setTablesAndProjectionMapForStreamItemPhotos(qb);
26243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c = qb.query(mDb, new String[]{StreamItemPhotos._ID, StreamItems.RAW_CONTACT_ID},
26253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selection, selectionArgs, null, null, null);
26263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
26273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            while (c.moveToNext()) {
26283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                streamItemPhotoIds.add(c.getLong(0));
26293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
26303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Throw a security exception if the account doesn't match the raw contact's.
26313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                enforceModifyingAccount(account, c.getLong(1));
26323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
26333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
26343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
26353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
26363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemPhotoIds;
26373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
26383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
26393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
26403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Queries the database for stream items under the given raw contact.  If there are
26413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * more entries than {@link ContactsProvider2#MAX_STREAM_ITEMS_PER_RAW_CONTACT},
26423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the oldest entries (as determined by timestamp) will be deleted.
26433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param rawContactId The raw contact ID to examine for stream items.
26443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param insertedStreamItemId The ID of the stream item that was just inserted,
26453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     prompting this cleanup.  Callers may pass 0 if no insertion prompted the
26463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     cleanup.
26473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The ID of the inserted stream item if it still exists after cleanup;
26483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     0 otherwise.
26493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
26503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long cleanUpOldStreamItems(long rawContactId, long insertedStreamItemId) {
26513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long postCleanupInsertedStreamId = insertedStreamItemId;
26523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c = mDb.query(Tables.STREAM_ITEMS, new String[]{StreamItems._ID},
26533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                StreamItems.RAW_CONTACT_ID + "=?", new String[]{String.valueOf(rawContactId)},
26543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                null, null, StreamItems.TIMESTAMP + " DESC, " + StreamItems._ID + " DESC");
26553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
26563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            int streamItemCount = c.getCount();
26573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if (streamItemCount <= MAX_STREAM_ITEMS_PER_RAW_CONTACT) {
26583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Still under the limit - nothing to clean up!
26593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return insertedStreamItemId;
26603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            } else {
26613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                c.moveToLast();
26623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                while (c.getPosition() >= MAX_STREAM_ITEMS_PER_RAW_CONTACT) {
26633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    long streamItemId = c.getLong(0);
26643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    if (insertedStreamItemId == streamItemId) {
26653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        // The stream item just inserted is being deleted.
26663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        postCleanupInsertedStreamId = 0;
26673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    }
26683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    deleteStreamItem(c.getLong(0));
26693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    c.moveToPrevious();
26703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
26713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
26723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
26733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
26743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
26753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return postCleanupInsertedStreamId;
26763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
26773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
2678ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
26798ab0b7a48efe540226253567bcf6fdbc487186a2Dmitri Plotnikov        mDbHelper.updateRawContactDisplayName(db, rawContactId);
2680d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    }
2681d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
26829261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
268320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
268420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
2685f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
268620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
268720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2688de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
2689de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
2690f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataRowHandler.DataDeleteQuery.COLUMNS,
2691f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                selection, selectionArgs, null);
2692de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        try {
2693de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            while(c.moveToNext()) {
2694f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                long rawContactId = c.getLong(DataRowHandler.DataDeleteQuery.RAW_CONTACT_ID);
269524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
269624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // Check for write profile permission if the data belongs to the profile.
2697afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForRawContact(mDb, rawContactId, true);
269824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2699f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
2700a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                DataRowHandler rowHandler = getDataRowHandler(mimeType);
2701d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                count += rowHandler.delete(mDb, mTransactionContext, c);
2702f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                if (!callerIsSyncAdapter) {
2703d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                    mTransactionContext.markRawContactDirty(rawContactId);
270488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                }
270520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
270620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
2707de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            c.close();
270820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
270920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
271020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
271120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
271220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
271388e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    /**
271488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     * Delete a data row provided that it is one of the allowed mime types.
271588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     */
271620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
2717f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
271888e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
271988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
27204da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(dataId);
2721f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataRowHandler.DataDeleteQuery.COLUMNS, Data._ID + "=?",
27224da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1, null);
2723f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
272420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
272520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
272620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
272720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
272820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2729f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov            String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
273020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
273120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
273220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
273320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
273420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
273520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
273620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
273720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
273820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
27397a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException("Data type mismatch: expected "
274020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
274120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
274220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
274324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Check for write profile permission if the data belongs to the profile.
274424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            long rawContactId = c.getLong(DataRowHandler.DataDeleteQuery.RAW_CONTACT_ID);
2745afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro            enforceProfilePermissionForRawContact(mDb, rawContactId, true);
274624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2747a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            DataRowHandler rowHandler = getDataRowHandler(mimeType);
2748d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            return rowHandler.delete(mDb, mTransactionContext, c);
274920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
275020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
275120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
275220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
275320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
275420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
2755ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
2756ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
2757f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private long insertGroup(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
2758f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2759f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2760f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2761e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final Account account = resolveAccount(uri, mValues);
2762ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2763ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
2764f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String packageName = mValues.getAsString(Groups.RES_PACKAGE);
276567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
2766f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(GroupsColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
276767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
2768f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.remove(Groups.RES_PACKAGE);
2769ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2770dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final boolean isFavoritesGroup = mValues.getAsLong(Groups.FAVORITES) != null
2771dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                ? mValues.getAsLong(Groups.FAVORITES) != 0
2772dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                : false;
2773dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2774f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2775f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(Groups.DIRTY, 1);
277673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
277773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2778f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long result = mDb.insert(Tables.GROUPS, Groups.TITLE, mValues);
2779ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2780dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter && isFavoritesGroup) {
2781dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // add all starred raw contacts to this group
2782dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String selection;
2783dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs;
2784dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (account == null) {
2785dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + " IS NULL AND "
2786dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + RawContacts.ACCOUNT_TYPE + " IS NULL";
2787dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = null;
2788dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
2789dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + "=? AND "
2790dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + RawContacts.ACCOUNT_TYPE + "=?";
2791dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = new String[]{account.name, account.type};
2792dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2793dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            Cursor c = mDb.query(Tables.RAW_CONTACTS,
2794dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[]{RawContacts._ID, RawContacts.STARRED},
2795dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    selection, selectionArgs, null, null, null);
2796892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            try {
2797892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                while (c.moveToNext()) {
2798892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (c.getLong(1) != 0) {
2799892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        final long rawContactId = c.getLong(0);
2800892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        insertDataGroupMembership(rawContactId, result);
2801d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                        mTransactionContext.markRawContactDirty(rawContactId);
2802892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    }
2803dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
2804892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            } finally {
2805892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                c.close();
2806dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2807dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2808dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2809f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mValues.containsKey(Groups.GROUP_VISIBLE)) {
28101a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2811ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        }
2812ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2813ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        return result;
2814ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2815ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
28165aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private long insertSettings(Uri uri, ContentValues values) {
2817e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final long id = mDb.insert(Tables.SETTINGS, null, values);
28185aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
28191a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
28201a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2821e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
28221a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
2823e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return id;
2824e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2825e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2826ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
282782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov     * Inserts a status update.
28281f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
282982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    public long insertStatusUpdate(ContentValues values) {
283082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        final String handle = values.getAsString(StatusUpdates.IM_HANDLE);
28310a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        final Integer protocol = values.getAsInteger(StatusUpdates.PROTOCOL);
28324dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        String customProtocol = null;
28334dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
28340a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        if (protocol != null && protocol == Im.PROTOCOL_CUSTOM) {
283582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            customProtocol = values.getAsString(StatusUpdates.CUSTOM_PROTOCOL);
28364dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (TextUtils.isEmpty(customProtocol)) {
28374dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                throw new IllegalArgumentException(
28384dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                        "CUSTOM_PROTOCOL is required when PROTOCOL=PROTOCOL_CUSTOM");
28394dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
28401f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
28411f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2842dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long rawContactId = -1;
2843dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long contactId = -1;
284482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Long dataId = values.getAsLong(StatusUpdates.DATA_ID);
28456802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        String accountType = null;
28466802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        String accountName = null;
2847f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.setLength(0);
28482526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov        mSelectionArgs.clear();
2849dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        if (dataId != null) {
2850dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the contact info for the given data row.
2851dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
28522526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSb.append(Tables.DATA + "." + Data._ID + "=?");
28532526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSelectionArgs.add(String.valueOf(dataId));
28541f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
2855dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the data row to attach this presence update to
2856dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
28570a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(handle) || protocol == null) {
28580a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                throw new IllegalArgumentException("PROTOCOL and IM_HANDLE are required");
28590a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
28600a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2861dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // TODO: generalize to allow other providers to match against email
2862dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == protocol;
2863dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
28642a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov            String mimeTypeIdIm = String.valueOf(mDbHelper.getMimeTypeIdForIm());
2865dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            if (matchEmail) {
28662a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                String mimeTypeIdEmail = String.valueOf(mDbHelper.getMimeTypeIdForEmail());
2867f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
2868f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // The following hack forces SQLite to use the (mimetype_id,data1) index, otherwise
2869f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the "OR" conjunction confuses it and it switches to a full scan of
2870f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the raw_contacts table.
2871f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
2872f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // This code relies on the fact that Im.DATA and Email.DATA are in fact the same
2873f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // column - Data.DATA1
28742526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + " IN (?,?)" +
28752526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Data.DATA1 + "=?" +
28762526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND ((" + DataColumns.MIMETYPE_ID + "=? AND " + Im.PROTOCOL + "=?");
28772526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
28782526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
28792526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
28802526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
28812526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
2882dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
28832526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
28842526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
2885dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
28862526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(") OR (" + DataColumns.MIMETYPE_ID + "=?))");
28872526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
2888dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            } else {
28892526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + "=?" +
28902526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.PROTOCOL + "=?" +
28912526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.DATA + "=?");
28922526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
28932526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
28942526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
2895dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
28962526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
28972526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
2898dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
2899dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
29001f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
290182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.DATA_ID)) {
29022526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(" AND " + DataColumns.CONCRETE_ID + "=?");
29032526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(values.getAsString(StatusUpdates.DATA_ID));
2904dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
290570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
290670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
29071f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
29081f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
2909de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            cursor = mDb.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
29102526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.toString(), mSelectionArgs.toArray(EMPTY_STRING_ARRAY), null, null,
29114394086494fe7909aaca70f56fb4bb08beebf303Dmitri Plotnikov                    Clauses.CONTACT_VISIBLE + " DESC, " + Data.RAW_CONTACT_ID);
29121f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
291367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
29145ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
29156802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                accountType = cursor.getString(DataContactsQuery.ACCOUNT_TYPE);
29166802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                accountName = cursor.getString(DataContactsQuery.ACCOUNT_NAME);
2917e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
29181f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
29191f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
29201f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
29211f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
29221f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
292331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
292431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
292531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
29261f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
29271f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
292882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.PRESENCE)) {
2929a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (customProtocol == null) {
2930a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // We cannot allow a null in the custom protocol field, because SQLite3 does not
2931a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // properly enforce uniqueness of null values
2932a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                customProtocol = "";
2933a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
2934a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2935a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.clear();
293682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.DATA_ID, dataId);
2937a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.RAW_CONTACT_ID, rawContactId);
2938a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.CONTACT_ID, contactId);
293982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PROTOCOL, protocol);
294082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
294182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.IM_HANDLE, handle);
294282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.IM_ACCOUNT)) {
294382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                mValues.put(StatusUpdates.IM_ACCOUNT, values.getAsString(StatusUpdates.IM_ACCOUNT));
2944a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
294582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PRESENCE,
294682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    values.getAsString(StatusUpdates.PRESENCE));
2947aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori            mValues.put(StatusUpdates.CHAT_CAPABILITY,
2948aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                    values.getAsString(StatusUpdates.CHAT_CAPABILITY));
29491f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2950a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            // Insert the presence update
2951a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mDb.replace(Tables.PRESENCE, null, mValues);
2952a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
2953e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
29540a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
295582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.STATUS)) {
295682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String status = values.getAsString(StatusUpdates.STATUS);
29570a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String resPackage = values.getAsString(StatusUpdates.STATUS_RES_PACKAGE);
29580a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Integer labelResource = values.getAsInteger(StatusUpdates.STATUS_LABEL);
29590a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
29600a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(resPackage)
29610a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && (labelResource == null || labelResource == 0)
29620a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && protocol != null) {
29630a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                labelResource = Im.getProtocolLabelResource(protocol);
29640a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
29650a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
29660a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Long iconResource = values.getAsLong(StatusUpdates.STATUS_ICON);
29670a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            // TODO compute the default icon based on the protocol
29680a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2969a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (TextUtils.isEmpty(status)) {
297078fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                mDbHelper.deleteStatusUpdate(dataId);
2971a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            } else {
29726802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                Long timestamp = values.getAsLong(StatusUpdates.STATUS_TIMESTAMP);
29736802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (timestamp != null) {
29746802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    mDbHelper.replaceStatusUpdate(dataId, timestamp, status, resPackage,
29756802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            iconResource, labelResource);
29766802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                } else {
29776802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    mDbHelper.insertStatusUpdate(dataId, status, resPackage, iconResource,
29786802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            labelResource);
29796802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                }
29806802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
29816802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // For forward compatibility with the new stream item API, insert this status update
29826802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // there as well.  If we already have a stream item from this source, update that
29836802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // one instead of inserting a new one (since the semantics of the old status update
29846802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // API is to only have a single record).
29856802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (rawContactId != -1 && !TextUtils.isEmpty(status)) {
29866802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    ContentValues streamItemValues = new ContentValues();
29876802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RAW_CONTACT_ID, rawContactId);
29886802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.TEXT, status);
29896802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.COMMENTS, "");
29906802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_PACKAGE, resPackage);
29916802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_ICON, iconResource);
29926802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_LABEL, labelResource);
29936802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.TIMESTAMP,
29946802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            timestamp == null ? System.currentTimeMillis() : timestamp);
29956802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
29966802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // Note: The following is basically a workaround for the fact that status
29976802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // updates didn't do any sort of account enforcement, while social stream item
29986802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // updates do.  We can't expect callers of the old API to start passing account
29996802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // information along, so we just populate the account params appropriately for
30006802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // the raw contact.
30016802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    if (accountName != null && accountType != null) {
30026802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        streamItemValues.put(RawContacts.ACCOUNT_NAME, accountName);
30036802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        streamItemValues.put(RawContacts.ACCOUNT_TYPE, accountType);
30046802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    }
30056802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
30066802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // Check for an existing stream item from this source, and insert or update.
30076802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    Uri streamUri = StreamItems.CONTENT_URI;
30086802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    Cursor c = query(streamUri, new String[]{StreamItems._ID},
30096802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            StreamItems.RAW_CONTACT_ID + "=?",
30106802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            new String[]{String.valueOf(rawContactId)}, null);
30116802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    try {
30126802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        if (c.getCount() > 0) {
30136802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            c.moveToFirst();
30146802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            update(ContentUris.withAppendedId(streamUri, c.getLong(0)),
30156802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    streamItemValues, null, null);
30166802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        } else {
30176802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            insert(streamUri, streamItemValues);
30186802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        }
30196802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    } finally {
30206802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        c.close();
30216802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    }
30226802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
30236802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                }
3024e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            }
3025e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        }
3026bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
3027a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (contactId != -1) {
3028f4015ab9ab7c26b766b5331fbf6655b8c54877eaDmitri Plotnikov            mContactAggregator.updateLastStatusUpdateId(contactId);
3029a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3030a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3031a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        return dataId;
30321f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
30331f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
30344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3035de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
3036bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3037b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "deleteInTransaction: " + uri);
3038b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3039b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3040f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3041f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
3042508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
3043508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
304435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3045b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs);
304635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3047b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID:
3048b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3049b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3050b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
3051b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selectionWithId, selectionArgs);
3052b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3053cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            case CONTACTS: {
3054cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // TODO
3055cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return 0;
3056cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3057cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3058d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
3059d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3060dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
30616bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
30626bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
30639fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP: {
30642e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
30652e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
30662e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
3067fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
3068fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
30692e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
30702e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
30712e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
3072dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
30732e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
30742e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
30759fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP_ID: {
30769fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                // lookup contact by id and lookup key to see if they still match the actual record
30779fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final List<String> pathSegments = uri.getPathSegments();
30789fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final String lookupKey = pathSegments.get(2);
30799fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
30809fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                setTablesAndProjectionMapForContacts(lookupQb, uri, null);
3081a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
30829fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                String[] args;
30839fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                if (selectionArgs == null) {
30849fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[2];
30859fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } else {
30869fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[selectionArgs.length + 2];
30879fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
30889fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
30899fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                args[0] = String.valueOf(contactId);
309060de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann                args[1] = Uri.encode(lookupKey);
30919fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                lookupQb.appendWhere(Contacts._ID + "=? AND " + Contacts.LOOKUP_KEY + "=?");
30929fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final SQLiteDatabase db = mDbHelper.getReadableDatabase();
30939fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                Cursor c = query(db, lookupQb, null, selection, args, null, null, null);
30949fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                try {
30959fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    if (c.getCount() == 1) {
30969fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // contact was unmodified so go ahead and delete it
3097dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        return deleteContact(contactId, callerIsSyncAdapter);
30989fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    } else {
30999fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // row was changed (e.g. the merging might have changed), we got multiple
31009fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // rows or the supplied selection filtered the record out
31019fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        return 0;
31029fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    }
31039fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } finally {
31049fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    c.close();
31059fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
31069fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            }
31079fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
31082971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case RAW_CONTACTS: {
31092971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
3110fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                Cursor c = mDb.query(Tables.RAW_CONTACTS,
3111fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        new String[]{RawContacts._ID, RawContacts.CONTACT_ID},
3112e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
31132971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
31142971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
31152971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                        final long rawContactId = c.getLong(0);
3116fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        long contactId = c.getLong(1);
3117fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        numDeletes += deleteRawContact(rawContactId, contactId,
3118fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                                callerIsSyncAdapter);
31192971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
31202971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
31212971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
31222971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
31232971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
31242971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
31252971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
31265ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
31272971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                final long rawContactId = ContentUris.parseId(uri);
3128fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                return deleteRawContact(rawContactId, mDbHelper.getContactId(rawContactId),
3129fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        callerIsSyncAdapter);
3130508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3131508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
313220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
3133f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
3134944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                return deleteData(appendAccountToSelection(uri, selection), selectionArgs,
3135f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        callerIsSyncAdapter);
313620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
313720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
313848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
313948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
314048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
314148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
3142508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
3143f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
31444da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
31454da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                return deleteData(Data._ID + "=?", mSelectionArgs1, callerIsSyncAdapter);
3146ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3147ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3148ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3149f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
31505aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteGroup(uri, ContentUris.parseId(uri), callerIsSyncAdapter);
31512971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
31522971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
31532971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case GROUPS: {
31542971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
31552971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups._ID},
3156e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
31572971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
31582971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
31595aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        numDeletes += deleteGroup(uri, c.getLong(0), callerIsSyncAdapter);
31602971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
31612971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
31622971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
31632971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
316481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (numDeletes > 0) {
3165f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
316681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
31672971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
3168508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3169508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
3170eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
317143880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3172e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                return deleteSettings(uri, appendAccountToSelection(uri, selection), selectionArgs);
3173eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3174eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
317582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
31760a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                return deleteStatusUpdates(selection, selectionArgs);
31771f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
31781f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
31793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
31803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
31813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItems(uri, new ContentValues(), selection, selectionArgs);
31823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
31833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
31843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
31853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
31863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItems(uri, new ContentValues(),
31873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemsColumns.CONCRETE_ID + "=?",
31883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{uri.getLastPathSegment()});
31893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
31903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
31913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
31923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
31933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItemPhotos(uri, new ContentValues(), selection, selectionArgs);
31943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
31953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
31963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
31973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
31983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
31993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
32003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItemPhotos(uri, new ContentValues(),
32013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=? AND "
32023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                                + StreamItemPhotos.STREAM_ITEM_ID + "=?",
32033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{streamItemPhotoId, streamItemId});
32043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
32053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
320681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
320781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
32083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
320981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
3210508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
32114f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
32124f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
32131c8e40c18f92722b9bec6e8ce2e345a9828efa16Dmitri Plotnikov    public int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) {
3214ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
3215b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final long groupMembershipMimetypeId = mDbHelper
321694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
3217de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mDb.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
321894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
321994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupId, null);
322094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
322194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        try {
3222f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (callerIsSyncAdapter) {
3223de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
322494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            } else {
322594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.clear();
322694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.put(Groups.DELETED, 1);
3227f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mValues.put(Groups.DIRTY, 1);
3228de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId, null);
322994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            }
323094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        } finally {
32311a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
323294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
323394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
323494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
32355aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
3236e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.delete(Tables.SETTINGS, selection, selectionArgs);
32371a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        mVisibleTouched = true;
3238e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3239e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3240e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3241dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int deleteContact(long contactId, boolean callerIsSyncAdapter) {
3242afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        enforceProfilePermissionForContact(mDb, contactId, true);
324396b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(contactId);
3244cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
324596b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                RawContacts.CONTACT_ID + "=?", mSelectionArgs1,
324696b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                null, null, null);
3247cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        try {
3248cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            while (c.moveToNext()) {
3249cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                long rawContactId = c.getLong(0);
3250dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
3251cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3252cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } finally {
3253cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            c.close();
3254cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
3255cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
32563826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
32573826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
3258cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return mDb.delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
3259cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3260cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3261fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov    public int deleteRawContact(long rawContactId, long contactId, boolean callerIsSyncAdapter) {
3262afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        enforceProfilePermissionForRawContact(mDb, rawContactId, true);
32633389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
32643826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
32653826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
3266f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (callerIsSyncAdapter) {
326714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            mDb.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
3268fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            int count = mDb.delete(Tables.RAW_CONTACTS, RawContacts._ID + "=" + rawContactId, null);
3269fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            mContactAggregator.updateDisplayNameForContact(mDb, contactId);
3270fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return count;
327133b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } else {
3272b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.removeContactIfSingleton(rawContactId);
3273dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
327433b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        }
327533b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
327633b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
32770a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private int deleteStatusUpdates(String selection, String[] selectionArgs) {
32789705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // delete from both tables: presence and status_updates
32799705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // TODO should account type/name be appended to the where clause?
32809705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      if (VERBOSE_LOGGING) {
32819705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          Log.v(TAG, "deleting data from status_updates for " + selection);
32829705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      }
32839705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      mDb.delete(Tables.STATUS_UPDATES, getWhereClauseForStatusUpdatesTable(selection),
32849705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          selectionArgs);
32859705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      return mDb.delete(Tables.PRESENCE, selection, selectionArgs);
32860a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
32870a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
32883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItems(Uri uri, ContentValues values, String selection,
32893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
32903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // First query for the stream items to be deleted, and check that they belong
32913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // to the account.
32923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
32933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemIds = enforceModifyingAccountForStreamItems(
32943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                account, selection, selectionArgs);
32953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
32963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If no security exception has been thrown, we're fine to delete.
32973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        for (long streamItemId : streamItemIds) {
32983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            deleteStreamItem(streamItemId);
32993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
33003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
33013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mVisibleTouched = true;
33023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemIds.size();
33033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
33043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
33053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItem(long streamItemId) {
33063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Note that this does not enforce the modifying account.
33073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        deleteStreamItemPhotos(streamItemId);
33083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return mDb.delete(Tables.STREAM_ITEMS, StreamItems._ID + "=?",
33093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{String.valueOf(streamItemId)});
33103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
33113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
33123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItemPhotos(Uri uri, ContentValues values, String selection,
33133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
33143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // First query for the stream item photos to be deleted, and check that they
33153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // belong to the account.
33163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
33173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItemPhotos(account, selection, selectionArgs);
33183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
33193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If no security exception has been thrown, we're fine to delete.
33203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return mDb.delete(Tables.STREAM_ITEM_PHOTOS, selection, selectionArgs);
33213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
33223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
33233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItemPhotos(long streamItemId) {
33243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Note that this does not enforce the modifying account.
33253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return mDb.delete(Tables.STREAM_ITEM_PHOTOS, StreamItemPhotos.STREAM_ITEM_ID + "=?",
33263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{String.valueOf(streamItemId)});
33273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
33283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
3329dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int markRawContactAsDeleted(long rawContactId, boolean callerIsSyncAdapter) {
333081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = true;
333181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
3332cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.clear();
3333cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DELETED, 1);
3334cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
3335cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
3336cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
3337cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DIRTY, 1);
3338dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        return updateRawContact(rawContactId, mValues, callerIsSyncAdapter);
3339cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3340cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
33414f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3342de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
3343de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            String[] selectionArgs) {
3344bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3345b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "updateInTransaction: " + uri);
3346b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3347b5a4add17815167d20a90645779df34cdf45280dFred Quintana
334835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
334900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
335000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
3351b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (match == SYNCSTATE_ID && selection == null) {
3352b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long rowId = ContentUris.parseId(uri);
33531129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Object data = values.get(ContactsContract.SyncState.DATA);
3354d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            mTransactionContext.syncStateUpdated(rowId, data);
3355b5a4add17815167d20a90645779df34cdf45280dFred Quintana            return 1;
3356b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3357b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3358f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3359f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
336000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
336135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3362b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
3363b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        appendAccountToSelection(uri, selection), selectionArgs);
3364b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3365b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID: {
3366b5a4add17815167d20a90645779df34cdf45280dFred Quintana                selection = appendAccountToSelection(uri, selection);
3367b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3368b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3369b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
3370b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
3371b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        selectionWithId, selectionArgs);
3372b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
337335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3374d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
3375dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(values, selection, selectionArgs, callerIsSyncAdapter);
337600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
337700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
337800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3379d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
3380dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(ContentUris.parseId(uri), values, callerIsSyncAdapter);
3381c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
3382c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
3383c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
338424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
338524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // Restrict update to the user's profile.
338624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                StringBuilder profileSelection = new StringBuilder();
338724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                profileSelection.append(Contacts.IS_USER_PROFILE + "=1");
338824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                if (!TextUtils.isEmpty(selection)) {
338924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    profileSelection.append(" AND (").append(selection).append(")");
339024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                }
339124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                count = updateContactOptions(values, profileSelection.toString(), selectionArgs,
339224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        callerIsSyncAdapter);
339324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
339424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
339524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
33962e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
33972e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
33982e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
33992e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
34002e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
3401fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
3402fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
34032e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
34042e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
34052e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
3406dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(contactId, values, callerIsSyncAdapter);
34072e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                break;
34082e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
34092e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
34107d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            case RAW_CONTACTS_DATA: {
34117d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                final String rawContactId = uri.getPathSegments().get(1);
34127d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
34137d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                    + (selection == null ? "" : " AND " + selection);
34147d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
34157d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter);
34167d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
34177d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                break;
34187d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            }
34197d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
342020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
3421944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                count = updateData(uri, values, appendAccountToSelection(uri, selection),
3422f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
342381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3424f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
342581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
342620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
342720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3428c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
342948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
343048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
343148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
343248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
3433f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
343481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3435f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
343681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
343700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
343800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
34397e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
34405ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
34415ac5c70d1165309302ebcc931f51723e37d31e0bJeff Sharkey                selection = appendAccountToSelection(uri, selection);
3442dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateRawContacts(values, selection, selectionArgs, callerIsSyncAdapter);
34437e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
34447e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
34457e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
34465ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
344733b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
34484529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                if (selection != null) {
34494da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
34504da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=?"
3451dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                                    + " AND(" + selection + ")", selectionArgs,
3452dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
34534529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                } else {
34544da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(rawContactId);
3455dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    count = updateRawContacts(values, RawContacts._ID + "=?", mSelectionArgs1,
3456dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
34574529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                }
34587e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
34597e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
34607e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
3461ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
34625aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, appendAccountToSelection(uri, selection),
3463f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
346481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3465f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
346681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3467ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3468ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3469ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3470ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3471ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
34724da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(groupId));
34734da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                String selectionWithId = Groups._ID + "=? "
347473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                        + (selection == null ? "" : " AND " + selection);
34755aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, selectionWithId, selectionArgs,
34765aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        callerIsSyncAdapter);
347781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3478f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
347981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3480ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3481ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3482ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3483127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
3484de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                count = updateAggregationException(mDb, values);
3485b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
3486b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
3487b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
3488eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
3489e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                count = updateSettings(uri, values, appendAccountToSelection(uri, selection),
3490e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        selectionArgs);
349143880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3492eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
3493eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3494eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
34959705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            case STATUS_UPDATES: {
34969705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                count = updateStatusUpdate(uri, values, selection, selectionArgs);
34979705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                break;
34989705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            }
34999705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
35003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
35013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItems(uri, values, selection, selectionArgs);
35023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
35033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
35043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
35053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
35063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItems(uri, values, StreamItemsColumns.CONCRETE_ID + "=?",
35073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{uri.getLastPathSegment()});
35083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
35093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
35103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
35113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
35123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values, selection, selectionArgs);
35133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
35143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
35153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
35163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
35173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
35183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values,
35193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotos.STREAM_ITEM_ID + "=?", new String[]{streamItemId});
35203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
35213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
35223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
35233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
35243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
35253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
35263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values,
35273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=? AND " +
35283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                                StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=?",
35293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{streamItemPhotoId, streamItemId});
35303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
35313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
35323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
353372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            case DIRECTORIES: {
3534bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                mContactDirectoryManager.scanPackagesByUid(Binder.getCallingUid());
353572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                count = 1;
3536d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
3537d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
3538d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
353946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            case DATA_USAGE_FEEDBACK_ID: {
354046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (handleDataUsageFeedback(uri)) {
354146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    count = 1;
354246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                } else {
354346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    count = 0;
354446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
354546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                break;
354646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
354746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
354881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
354981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
3550f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
355181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
355200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
355300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
355400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
35554f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
35564f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
35579705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private int updateStatusUpdate(Uri uri, ContentValues values, String selection,
35589705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        String[] selectionArgs) {
35599705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // update status_updates table, if status is provided
35609705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO should account type/name be appended to the where clause?
35619705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        int updateCount = 0;
35629705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContentValues settableValues = getSettableColumnsForStatusUpdatesTable(values);
35639705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
35649705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.STATUS_UPDATES,
35659705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    settableValues,
35669705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    getWhereClauseForStatusUpdatesTable(selection),
35679705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selectionArgs);
35689705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
35699705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
35709705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // now update the Presence table
35719705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        settableValues = getSettableColumnsForPresenceTable(values);
35729705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
35739705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.PRESENCE, settableValues,
35749705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selection, selectionArgs);
35759705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
35769705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO updateCount is not entirely a valid count of updated rows because 2 tables could
35779705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // potentially get updated in this method.
35789705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return updateCount;
35799705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
35809705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
35813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int updateStreamItems(Uri uri, ContentValues values, String selection,
35823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
35833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Stream items can't be moved to a new raw contact.
35843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        values.remove(StreamItems.RAW_CONTACT_ID);
35853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
35863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check that the stream items being updated belong to the account.
35873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
35883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItems(account, selection, selectionArgs);
35893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
35906802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to update accounts params - they don't exist in the stream items table.
35916802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_NAME);
35926802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_TYPE);
35936802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
35943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If there's been no exception, the update should be fine.
35953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return mDb.update(Tables.STREAM_ITEMS, values, selection, selectionArgs);
35963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
35973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
35983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int updateStreamItemPhotos(Uri uri, ContentValues values, String selection,
35993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
36003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Stream item photos can't be moved to a new stream item.
36013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        values.remove(StreamItemPhotos.STREAM_ITEM_ID);
36023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
36033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check that the stream item photos being updated belong to the account.
36043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
36053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItemPhotos(account, selection, selectionArgs);
36063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
36076802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to update accounts params - they don't exist in the stream item
36086802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // photos table.
36096802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_NAME);
36106802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_TYPE);
36116802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
36126802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Process the photo (since we're updating, it's valid for the photo to not be present).
36136802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (processStreamItemPhoto(values, true)) {
36146802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // If there's been no exception, the update should be fine.
36156802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return mDb.update(Tables.STREAM_ITEM_PHOTOS, values, selection, selectionArgs);
36166802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
36176802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        return 0;
36183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
36193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
36209705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    /**
36219705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     * Build a where clause to select the rows to be updated in status_updates table.
36229705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     */
36239705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private String getWhereClauseForStatusUpdatesTable(String selection) {
36249705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.setLength(0);
36259705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE);
36269705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(selection);
36279705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(")");
36289705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mSb.toString();
36299705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
36309705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
36319705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForStatusUpdatesTable(ContentValues values) {
36329705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
36339705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS, values,
36349705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS);
36359705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_TIMESTAMP, values,
36369705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_TIMESTAMP);
36379705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_RES_PACKAGE, values,
36389705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_RES_PACKAGE);
36399705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_LABEL, values,
36409705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_LABEL);
36419705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_ICON, values,
36429705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_ICON);
36439705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
36449705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
36459705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
36469705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForPresenceTable(ContentValues values) {
36479705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
36489705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.PRESENCE, values,
36499705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.PRESENCE);
3650aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.CHAT_CAPABILITY, values,
3651aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                StatusUpdates.CHAT_CAPABILITY);
36529705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
36539705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
36549705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
36555aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int updateGroups(Uri uri, ContentValues values, String selectionWithId,
3656f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
365773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3658ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
3659ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
366073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        ContentValues updatedValues;
3661f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter && !values.containsKey(Groups.DIRTY)) {
366273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = mValues;
366373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.clear();
366473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.putAll(values);
366573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.put(Groups.DIRTY, 1);
366673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } else {
366773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = values;
366873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
366973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3670ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        int count = mDb.update(Tables.GROUPS, updatedValues, selectionWithId, selectionArgs);
36711a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) {
36721a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
367394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
36746ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        if (updatedValues.containsKey(Groups.SHOULD_SYNC)
36751129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                && updatedValues.getAsInteger(Groups.SHOULD_SYNC) != 0) {
36766ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups.ACCOUNT_NAME,
3677e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    Groups.ACCOUNT_TYPE}, selectionWithId, selectionArgs, null,
36786ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    null, null);
36796ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountName;
36806ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountType;
36816ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            try {
36826ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                while (c.moveToNext()) {
36836ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountName = c.getString(0);
36846ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountType = c.getString(1);
368524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
36866ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        Account account = new Account(accountName, accountType);
3687ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        ContentResolver.requestSync(account, ContactsContract.AUTHORITY,
36886ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                                new Bundle());
36896ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        break;
36906ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    }
36916ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                }
36926ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            } finally {
36936ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                c.close();
36946ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            }
36956ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        }
369694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        return count;
369794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
369894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
3699b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private int updateSettings(Uri uri, ContentValues values, String selection,
3700b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            String[] selectionArgs) {
3701e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.update(Tables.SETTINGS, values, selection, selectionArgs);
37021a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
37031a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3704e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
3705e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3706e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3707e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3708dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContacts(ContentValues values, String selection, String[] selectionArgs,
3709dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
37104529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (values.containsKey(RawContacts.CONTACT_ID)) {
37114529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            throw new IllegalArgumentException(RawContacts.CONTACT_ID + " should not be included " +
37124529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    "in content values. Contact IDs are assigned automatically");
37134529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
371473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
371597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
371697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
371797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0");
371897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
371997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
37204529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = 0;
3721ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        Cursor cursor = mDb.query(Views.RAW_CONTACTS,
372251bf5ea9531b9da72caff607dbdf35fd6f61cbe2Jeff Sharkey                new String[] { RawContacts._ID }, selection,
37234529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                selectionArgs, null, null, null);
37244529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        try {
37254529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            while (cursor.moveToNext()) {
37264529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                long rawContactId = cursor.getLong(0);
3727dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContact(rawContactId, values, callerIsSyncAdapter);
37284529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count++;
37294529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            }
37304529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        } finally {
37314529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            cursor.close();
37324529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
37334529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
37344529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return count;
37354529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    }
37364529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
3737dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContact(long rawContactId, ContentValues values,
3738dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
373924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
374024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // Enforce profile permissions if the raw contact is in the user's profile.
3741afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        enforceProfilePermissionForRawContact(mDb, rawContactId, true);
374224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
374396b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        final String selection = RawContacts._ID + " = ?";
374496b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(rawContactId);
374519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final boolean requestUndoDelete = (values.containsKey(RawContacts.DELETED)
374619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                && values.getAsInteger(RawContacts.DELETED) == 0);
374719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int previousDeleted = 0;
3748ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType = null;
3749ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName = null;
375019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        if (requestUndoDelete) {
375119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            Cursor cursor = mDb.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS, selection,
375296b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                    mSelectionArgs1, null, null, null);
375319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            try {
375419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                if (cursor.moveToFirst()) {
375519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    previousDeleted = cursor.getInt(RawContactsQuery.DELETED);
3756ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountType = cursor.getString(RawContactsQuery.ACCOUNT_TYPE);
3757ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountName = cursor.getString(RawContactsQuery.ACCOUNT_NAME);
375819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                }
375919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            } finally {
376019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                cursor.close();
376119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
376219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
376319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT);
376419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        }
3765f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
376696b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        int count = mDb.update(Tables.RAW_CONTACTS, values, selection, mSelectionArgs1);
37675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count != 0) {
3768f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            if (values.containsKey(RawContacts.AGGREGATION_MODE)) {
3769f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                int aggregationMode = values.getAsInteger(RawContacts.AGGREGATION_MODE);
3770f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov
3771f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // As per ContactsContract documentation, changing aggregation mode
3772f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // to DEFAULT should not trigger aggregation
3773f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                if (aggregationMode != RawContacts.AGGREGATION_MODE_DEFAULT) {
377469cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId, aggregationMode, false);
3775f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                }
3776f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            }
3777433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            if (values.containsKey(RawContacts.STARRED)) {
3778dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter) {
3779dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
3780dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            values.getAsLong(RawContacts.STARRED) != 0);
3781dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
37824529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                mContactAggregator.updateStarred(rawContactId);
3783dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
3784dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // if this raw contact is being associated with an account, then update the
3785dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // favorites group membership based on whether or not this contact is starred.
3786dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // If it is starred, add a group membership, if one doesn't already exist
3787dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // otherwise delete any matching group memberships.
3788dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
3789dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    boolean starred = 0 != DatabaseUtils.longForQuery(mDb,
3790dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            SELECTION_STARRED_FROM_RAW_CONTACTS,
3791dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            new String[]{Long.toString(rawContactId)});
3792dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId, starred);
3793dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
3794dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
3795dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3796dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // if this raw contact is being associated with an account, then add a
3797dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // group membership to the group marked as AutoAdd, if any.
3798dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
3799dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                addAutoAddMembership(rawContactId);
3800433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            }
3801dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3802285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (values.containsKey(RawContacts.SOURCE_ID)) {
38032b7a632bba423357ae5641f94da6a2f71afc523bDmitri Plotnikov                mContactAggregator.updateLookupKeyForRawContact(mDb, rawContactId);
3804285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
3805f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            if (values.containsKey(RawContacts.NAME_VERIFIED)) {
3806f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
3807f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // If setting NAME_VERIFIED for this raw contact, reset it for all
3808f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // other raw contacts in the same aggregate
3809f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                if (values.getAsInteger(RawContacts.NAME_VERIFIED) != 0) {
381078fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                    mDbHelper.resetNameVerifiedForOtherRawContacts(rawContactId);
3811f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                }
3812f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                mContactAggregator.updateDisplayNameForRawContact(mDb, rawContactId);
3813f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            }
381419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            if (requestUndoDelete && previousDeleted == 1) {
3815d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                mTransactionContext.rawContactInserted(rawContactId,
3816d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                        new Account(accountName, accountType));
381719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
38185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
38195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return count;
382033b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
382133b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
3822321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(Uri uri, ContentValues values, String selection,
3823f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
382420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
382520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
382620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
38275ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
382820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
382920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
383020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
383120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
383220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
3833b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
383420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
383520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
383697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
383797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
383897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    Data.IS_READ_ONLY + "=0");
383997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
384097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
3841653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        int count = 0;
384220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3843653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
3844653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // so we don't need to worry about updating data we don't have permission to read.
3845f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // This query will be allowed to return profiles, and we'll do the permission check
3846f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // within the loop.
38476ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro        Cursor c = queryLocal(uri.buildUpon()
3848f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                .appendQueryParameter(ContactsContract.ALLOW_PROFILE, "1").build(),
3849f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                DataRowHandler.DataUpdateQuery.COLUMNS,
38506ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                selection, selectionArgs, null, -1 /* directory ID */,
38516ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                true /* suppress profile check */);
3852653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        try {
3853653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            while(c.moveToNext()) {
385424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // Check profile permission for the raw contact that owns each data record.
385524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = c.getLong(DataRowHandler.DataUpdateQuery.RAW_CONTACT_ID);
3856afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForRawContact(mDb, rawContactId, true);
385724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
3858f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count += updateData(mValues, c, callerIsSyncAdapter);
385920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3860653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        } finally {
3861653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            c.close();
386220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
386320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3864653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return count;
386520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
386620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3867f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
3868653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        if (values.size() == 0) {
3869653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return 0;
3870321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
3871653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
3872f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        final String mimeType = c.getString(DataRowHandler.DataUpdateQuery.MIMETYPE);
3873a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
3874f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        boolean updated =
3875f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                rowHandler.update(mDb, mTransactionContext, values, c, callerIsSyncAdapter);
3876f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
3877f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
3878a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
3879f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        return updated ? 1 : 0;
3880321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    }
3881321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
38828c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(ContentValues values, String selection,
3883dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
38848c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        int count = 0;
3885ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        Cursor cursor = mDb.query(Views.CONTACTS,
388624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                new String[] { Contacts._ID, Contacts.IS_USER_PROFILE }, selection,
38878c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                selectionArgs, null, null, null);
38888c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        try {
38898c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            while (cursor.moveToNext()) {
38908c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                long contactId = cursor.getLong(0);
389124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
389224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // Check for profile write permission before updating a user's profile contact.
389324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                boolean isProfile = cursor.getInt(1) == 1;
389424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                if (isProfile) {
389524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    enforceProfilePermission(true);
389624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                }
389724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
3898dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateContactOptions(contactId, values, callerIsSyncAdapter);
38998c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count++;
39008c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            }
39018c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        } finally {
39028c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            cursor.close();
39038c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        }
39048c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
39058c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return count;
39068c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
39078c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
3908dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateContactOptions(long contactId, ContentValues values,
3909dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
3910d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
391124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // Check write permission if the contact is the user's profile.
3912afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        enforceProfilePermissionForContact(mDb, contactId, true);
391324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
39148c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
3915b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
3916d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
3917b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
3918d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
3919b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
3920d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
3921b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
3922d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
3923b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
3924d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.STARRED);
3925d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3926d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
39278c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.size() == 0) {
3928d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
3929d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
3930d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
39318c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.containsKey(RawContacts.STARRED)) {
3932c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            // Mark dirty when changing starred to trigger sync
39338c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            mValues.put(RawContacts.DIRTY, 1);
3934c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey        }
3935c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey
39364da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(contactId);
393797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        mDb.update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=?"
393897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                + " AND " + RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0", mSelectionArgs1);
39398c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
3940dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (mValues.containsKey(RawContacts.STARRED) && !callerIsSyncAdapter) {
3941ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann            Cursor cursor = mDb.query(Views.RAW_CONTACTS,
3942dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[] { RawContacts._ID }, RawContacts.CONTACT_ID + "=?",
3943dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    mSelectionArgs1, null, null, null);
3944dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            try {
3945dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                while (cursor.moveToNext()) {
3946dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    long rawContactId = cursor.getLong(0);
3947dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
3948dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            mValues.getAsLong(RawContacts.STARRED) != 0);
3949dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
3950dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } finally {
3951dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                cursor.close();
3952dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
3953dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
3954dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
39558c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // Copy changeable values to prevent automatically managed fields from
39568c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // being explicitly updated by clients.
39578c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
3958b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
39598c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
3960b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
39618c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
3962b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
39638c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
3964b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
39658c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
3966b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
39678c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.STARRED);
39688c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
39699b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        int rslt = mDb.update(Tables.CONTACTS, mValues, Contacts._ID + "=?", mSelectionArgs1);
39706e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori
39719b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        if (values.containsKey(Contacts.LAST_TIME_CONTACTED) &&
39729b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori                !values.containsKey(Contacts.TIMES_CONTACTED)) {
39739b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
39749b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
39759b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        }
39769b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        return rslt;
3977f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
3978d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3979127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
3980127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
39810c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId1 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID1);
39820c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId2 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID2);
398380c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
3984ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId1;
3985ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId2;
39860c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (rcId1 < rcId2) {
39870c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId1;
39880c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId2;
39890c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
39900c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId1;
39910c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId2;
3992b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
3993127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
39940c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
39954da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[0] = String.valueOf(rawContactId1);
39964da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[1] = String.valueOf(rawContactId2);
39970c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.delete(Tables.AGGREGATION_EXCEPTIONS,
39984da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    AggregationExceptions.RAW_CONTACT_ID1 + "=? AND "
39994da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    + AggregationExceptions.RAW_CONTACT_ID2 + "=?", mSelectionArgs2);
40000c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
40016bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            ContentValues exceptionValues = new ContentValues(3);
40026bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
40030c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
40040c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
40050c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
40060c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    exceptionValues);
4007127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
4008127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
40093389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
401069cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId1,
401169cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
401269cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId2,
401369cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
4014dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov
4015bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        mContactAggregator.aggregateContact(mTransactionContext, db, rawContactId1);
4016bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        mContactAggregator.aggregateContact(mTransactionContext, db, rawContactId2);
4017127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
4018127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
4019127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
4020127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
4021b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
4022b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
402370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    public void onAccountsUpdated(Account[] accounts) {
4024bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
40253826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
40263826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
4027bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected boolean updateAccountsInBackground(Account[] accounts) {
4028f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao        // TODO : Check the unit test.
4029e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov        boolean accountsChanged = false;
4030627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        HashSet<Account> existingAccounts = new HashSet<Account>();
403149d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov        mDb = mDbHelper.getWritableDatabase();
403270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        mDb.beginTransaction();
403370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        try {
4034dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            findValidAccounts(existingAccounts);
4035743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov
4036743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            // Add a row to the ACCOUNTS table for each new account
4037743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            for (Account account : accounts) {
4038743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                if (!existingAccounts.contains(account)) {
4039e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    accountsChanged = true;
4040743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                    mDb.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME
4041743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            + ", " + RawContacts.ACCOUNT_TYPE + ") VALUES (?, ?)",
4042743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            new String[] {account.name, account.type});
4043743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                }
4044743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            }
404548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
4046627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            // Remove all valid accounts from the existing account set. What is left
4047743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            // in the accountsToDelete set will be extra accounts whose data must be deleted.
4048627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            HashSet<Account> accountsToDelete = new HashSet<Account>(existingAccounts);
4049627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (Account account : accounts) {
4050627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                accountsToDelete.remove(account);
405170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
405270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
405333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            if (!accountsToDelete.isEmpty()) {
4054e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                accountsChanged = true;
4055e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                for (Account account : accountsToDelete) {
4056e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    Log.d(TAG, "removing data for removed account " + account);
4057e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    String[] params = new String[] {account.name, account.type};
4058e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4059e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.GROUPS +
4060e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Groups.ACCOUNT_NAME + " = ?" +
4061e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " AND " + Groups.ACCOUNT_TYPE + " = ?", params);
4062e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4063e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.PRESENCE +
4064e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (" +
4065e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    "SELECT " + RawContacts._ID +
4066e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " FROM " + Tables.RAW_CONTACTS +
4067e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
4068e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " AND " + RawContacts.ACCOUNT_TYPE + " = ?)", params);
4069e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4070e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.RAW_CONTACTS +
4071e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
4072e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + " = ?", params);
4073e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4074e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.SETTINGS +
4075e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Settings.ACCOUNT_NAME + " = ?" +
4076e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + Settings.ACCOUNT_TYPE + " = ?", params);
4077e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4078e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.ACCOUNTS +
4079e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + "=?" +
4080e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + "=?", params);
4081d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    mDb.execSQL(
4082d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            "DELETE FROM " + Tables.DIRECTORIES +
4083d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            " WHERE " + Directory.ACCOUNT_NAME + "=?" +
4084d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            " AND " + Directory.ACCOUNT_TYPE + "=?", params);
40854458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    resetDirectoryCache();
4086e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                }
4087e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov
408833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // Find all aggregated contacts that used to contain the raw contacts
408933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // we have just deleted and see if they are still referencing the deleted
4090e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                // names or photos.  If so, fix up those contacts.
409133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                HashSet<Long> orphanContactIds = Sets.newHashSet();
409233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                Cursor cursor = mDb.rawQuery("SELECT " + Contacts._ID +
409333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " FROM " + Tables.CONTACTS +
409433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " WHERE (" + Contacts.NAME_RAW_CONTACT_ID + " NOT NULL AND " +
409569cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                Contacts.NAME_RAW_CONTACT_ID + " NOT IN " +
409669cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + RawContacts._ID +
409769cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.RAW_CONTACTS + "))" +
409833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " OR (" + Contacts.PHOTO_ID + " NOT NULL AND " +
409933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                                Contacts.PHOTO_ID + " NOT IN " +
410069cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + Data._ID +
410169cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.DATA + "))", null);
410233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                try {
410333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    while (cursor.moveToNext()) {
410433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        orphanContactIds.add(cursor.getLong(0));
410533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    }
410633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                } finally {
410733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    cursor.close();
410833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
410933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
411033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                for (Long contactId : orphanContactIds) {
4111bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov                    mContactAggregator.updateAggregateData(mTransactionContext, contactId);
411233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
4113e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                mDbHelper.updateAllVisible();
4114bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov                updateSearchIndexInTransaction();
411533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            }
411633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
4117e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            if (accountsChanged) {
4118e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                mDbHelper.getSyncState().onAccountsChanged(mDb, accounts);
4119e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            }
412070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.setTransactionSuccessful();
412170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } finally {
412270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.endTransaction();
412370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
412473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.clear();
41253826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
41263826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (accountsChanged) {
41273826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateContactsAccountCount(accounts);
41283826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
41293826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
41303826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
4131afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov        return accountsChanged;
413270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
4133619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
41343826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateContactsAccountCount(Account[] accounts) {
41353826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        int count = 0;
41363826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        for (Account account : accounts) {
41373826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            if (isContactsAccount(account)) {
41383826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                count++;
41393826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            }
41403826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
41413826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mContactsAccountCount = count;
41423826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
41433826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
41443826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    protected boolean isContactsAccount(Account account) {
41453826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        final IContentService cs = ContentResolver.getContentService();
41463826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        try {
41473826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return cs.getIsSyncable(account, ContactsContract.AUTHORITY) > 0;
41483826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } catch (RemoteException e) {
41493826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            Log.e(TAG, "Cannot obtain sync flag for account: " + account, e);
41503826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return false;
41513826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
41523826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
41533826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
415472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void onPackageChanged(String packageName) {
4155bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_DIRECTORIES, packageName);
4156d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4157d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4158619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
4159627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     * Finds all distinct accounts present in the specified table.
4160627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     */
4161dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void findValidAccounts(Set<Account> validAccounts) {
4162743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov        Cursor c = mDb.rawQuery(
4163743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                "SELECT " + RawContacts.ACCOUNT_NAME + "," + RawContacts.ACCOUNT_TYPE +
4164743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                " FROM " + Tables.ACCOUNTS, null);
4165627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
4166627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            while (c.moveToNext()) {
4167dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!c.isNull(0) || !c.isNull(1)) {
4168627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    validAccounts.add(new Account(c.getString(0), c.getString(1)));
4169627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
4170627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
4171627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } finally {
4172627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            c.close();
4173627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
4174627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
4175627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
41764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
41774f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
41784f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
417915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
418015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mReadAccessLatch);
418115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
4182d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String directory = getQueryParameter(uri, ContactsContract.DIRECTORY_PARAM_KEY);
4183385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directory == null) {
41843716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            return wrapCursor(uri,
41856ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder, -1, false));
4186385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directory.equals("0")) {
41873716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            return wrapCursor(uri,
41883716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder,
41896ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                            Directory.DEFAULT, false));
4190d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        } else if (directory.equals("1")) {
41913716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            return wrapCursor(uri,
41923716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder,
41936ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                            Directory.LOCAL_INVISIBLE, false));
4194d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4195d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4196d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        DirectoryInfo directoryInfo = getDirectoryAuthority(directory);
4197d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo == null) {
4198a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            Log.e(TAG, "Invalid directory ID: " + uri);
4199a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            return null;
4200d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4201d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4202d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Builder builder = new Uri.Builder();
4203d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.scheme(ContentResolver.SCHEME_CONTENT);
4204d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.authority(directoryInfo.authority);
4205d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.encodedPath(uri.getEncodedPath());
4206d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountName != null) {
4207d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_NAME, directoryInfo.accountName);
4208d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4209d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountType != null) {
4210d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, directoryInfo.accountType);
4211d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
42122e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
42132e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limit = getLimit(uri);
42142e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        if (limit != null) {
42152e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov            builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, limit);
42162e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        }
42172e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
4218d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Uri directoryUri = builder.build();
421909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
422009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        if (projection == null) {
422109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            projection = getDefaultProjection(uri);
422209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
422309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
4224332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        Cursor cursor = getContext().getContentResolver().query(directoryUri, projection, selection,
4225d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                selectionArgs, sortOrder);
42266ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
42276ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (cursor == null) {
42286ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return null;
42296ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
42306ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
4231547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        CrossProcessCursor crossProcessCursor = getCrossProcessCursor(cursor);
4232547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        if (crossProcessCursor != null) {
4233547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro            return wrapCursor(uri, cursor);
4234547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        } else {
4235547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro            return matrixCursorFromCursor(wrapCursor(uri, cursor));
4236547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        }
42373716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    }
42383716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
4239547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro    private Cursor wrapCursor(Uri uri, Cursor cursor) {
4240547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro
4241547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        // If the cursor doesn't contain a snippet column, don't bother wrapping it.
4242547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        if (cursor.getColumnIndex(SearchSnippetColumns.SNIPPET) < 0) {
4243547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro            return cursor;
4244547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        }
4245547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro
42463716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // Parse out snippet arguments for use when snippets are retrieved from the cursor.
42473716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String[] args = null;
42483716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String snippetArgs =
42493716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                getQueryParameter(uri, SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY);
42503716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        if (snippetArgs != null) {
42513716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            args = snippetArgs.split(",");
42523716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        }
42533716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
42543716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String query = uri.getLastPathSegment();
42553716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String startMatch = args != null && args.length > 0 ? args[0]
42563716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_START_MATCH;
42573716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String endMatch = args != null && args.length > 1 ? args[1]
42583716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_END_MATCH;
42593716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String ellipsis = args != null && args.length > 2 ? args[2]
42603716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_ELLIPSIS;
42613716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        int maxTokens = args != null && args.length > 3 ? Integer.parseInt(args[3])
42623716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_MAX_TOKENS;
42633716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
4264547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        return new SnippetizingCursorWrapper(cursor, query, startMatch, endMatch, ellipsis,
4265547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro                maxTokens);
42666ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
42676ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
42686ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    private CrossProcessCursor getCrossProcessCursor(Cursor cursor) {
42696ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        Cursor c = cursor;
42706ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (c instanceof CrossProcessCursor) {
42716ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return (CrossProcessCursor) c;
42726ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else if (c instanceof CursorWindow) {
42736ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return getCrossProcessCursor(((CursorWrapper) c).getWrappedCursor());
42746ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else {
42756ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return null;
42766ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
42776ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
42786ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
42796ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    public MatrixCursor matrixCursorFromCursor(Cursor cursor) {
42806ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        MatrixCursor newCursor = new MatrixCursor(cursor.getColumnNames());
42816ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        int numColumns = cursor.getColumnCount();
42826ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        String data[] = new String[numColumns];
42836ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        cursor.moveToPosition(-1);
42846ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        while (cursor.moveToNext()) {
42856ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            for (int i = 0; i < numColumns; i++) {
42866ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov                data[i] = cursor.getString(i);
42876ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            }
42886ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            newCursor.addRow(data);
4289332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        }
42906ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        return newCursor;
4291d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4292d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4293d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final class DirectoryQuery {
4294d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
4295d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory._ID,
4296d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.DIRECTORY_AUTHORITY,
4297d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_NAME,
4298d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_TYPE
4299d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        };
4300d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4301d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int DIRECTORY_ID = 0;
4302d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int AUTHORITY = 1;
4303d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_NAME = 2;
4304d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_TYPE = 3;
4305d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4306d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4307d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
4308d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Reads and caches directory information for the database.
4309d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
4310d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private DirectoryInfo getDirectoryAuthority(String directoryId) {
43114458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized (mDirectoryCache) {
43124458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            if (!mDirectoryCacheValid) {
43134458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCache.clear();
431449d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                SQLiteDatabase db = mDbHelper.getReadableDatabase();
431549d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                Cursor cursor = db.query(Tables.DIRECTORIES,
43164458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryQuery.COLUMNS,
43174458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        null, null, null, null, null);
43184458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                try {
43194458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    while (cursor.moveToNext()) {
43204458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryInfo info = new DirectoryInfo();
43214458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        String id = cursor.getString(DirectoryQuery.DIRECTORY_ID);
43224458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.authority = cursor.getString(DirectoryQuery.AUTHORITY);
43234458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
43244458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
43254458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        mDirectoryCache.put(id, info);
43264458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    }
43274458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                } finally {
43284458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    cursor.close();
4329d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                }
43304458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCacheValid = true;
4331d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4332d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
43334458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            return mDirectoryCache.get(directoryId);
43344458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
4335d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4336d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
433772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void resetDirectoryCache() {
43384458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized(mDirectoryCache) {
43394458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            mDirectoryCacheValid = false;
43404458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
434172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
434272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
43436ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro    private Cursor queryLocal(Uri uri, String[] projection, String selection,
43446ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro            String[] selectionArgs, String sortOrder, long directoryId,
43454b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki            final boolean suppressProfileCheck) {
4346bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
4347bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            Log.v(TAG, "query: " + uri);
4348bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        }
43490b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov
4350b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
435135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4352d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
43531f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
4354c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limit = getLimit(uri);
4355c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
43564b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki        // Column name for appendProfileRestriction().  We append the profile check to the original
43574b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki        // selection if it's not null.
43584b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki        String profileRestrictionColumnName = null;
43594b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki
4360a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
43614f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
436235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
4363b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().query(db, projection, selection,  selectionArgs,
436435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                        sortOrder);
436535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4366d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
4367763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
43684b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                appendLocalDirectorySelectionIfNeeded(qb, directoryId);
43694b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                profileRestrictionColumnName = Contacts.IS_USER_PROFILE;
43706ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                sortOrder = prependProfileSortIfNeeded(uri, sortOrder, suppressProfileCheck);
4371619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
4372619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
4373619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
4374d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
43754a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
4376afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
4377763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
43784da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
43794da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
43806bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
43816bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
43826bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
43835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP:
43845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
43855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
43865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int segmentCount = pathSegments.size();
43875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount < 3) {
4388fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
4389fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
43905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
4391a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
43925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String lookupKey = pathSegments.get(2);
43935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount == 4) {
43945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
4395afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                    enforceProfilePermissionForContact(db, contactId, false);
43965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
4397763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
4398a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4399a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
4400a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4401a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
4402a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
44035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        return c;
44045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
44055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
44065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
4407763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
44084da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
44094da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
44104da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
44115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                break;
44125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
44135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
44142149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_DATA:
44152149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_DATA: {
44162149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
44172149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                int segmentCount = pathSegments.size();
44182149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount < 4) {
44192149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
44202149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                            "Missing a lookup key", uri));
44212149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
44222149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
44232149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount == 5) {
44242149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
4425afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                    enforceProfilePermissionForContact(db, contactId, false);
44262149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
44272149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    setTablesAndProjectionMapForData(lookupQb, uri, projection, false);
4428a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
4429a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
4430a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4431a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Data.CONTACT_ID, contactId, Data.LOOKUP_KEY, lookupKey);
4432a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
44332149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                        return c;
44342149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    }
44352149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
44362149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    // TODO see if the contact exists but has no data rows (rare)
44372149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
44382149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
44392149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
444024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long contactId = lookupContactIdByLookupKey(db, lookupKey);
4441afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
44422149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
444324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        String.valueOf(contactId));
44442149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                qb.appendWhere(" AND " + Data.CONTACT_ID + "=?");
44452149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                break;
44462149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            }
44472149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
44483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_ID_STREAM_ITEMS: {
44493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                long contactId = Long.parseLong(uri.getPathSegments().get(1));
4450afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
44513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
44523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
44533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(RawContactsColumns.CONCRETE_CONTACT_ID + "=?");
44543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
44553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
44563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
44573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_LOOKUP_STREAM_ITEMS:
44583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_LOOKUP_ID_STREAM_ITEMS: {
44593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                List<String> pathSegments = uri.getPathSegments();
44603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                int segmentCount = pathSegments.size();
44613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                if (segmentCount < 4) {
44623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
44633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                            "Missing a lookup key", uri));
44643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
44653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String lookupKey = pathSegments.get(2);
44663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                if (segmentCount == 5) {
44673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    long contactId = Long.parseLong(pathSegments.get(3));
4468afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                    enforceProfilePermissionForContact(db, contactId, false);
44693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
44703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    setTablesAndProjectionMapForStreamItems(lookupQb);
44713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
44723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
44733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                            RawContacts.CONTACT_ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
44743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    if (c != null) {
44753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        return c;
44763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    }
44773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
44783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
44793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
44803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                long contactId = lookupContactIdByLookupKey(db, lookupKey);
4481afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
44823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
44833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(RawContacts.CONTACT_ID + "=?");
44843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
44853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
44863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
4487f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
448842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
448924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long contactId = lookupContactIdByLookupKey(db, lookupKey);
4490afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
4491ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
4492f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                qb.setProjectionMap(sContactsVCardProjectionMap);
44934da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
449424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        String.valueOf(contactId));
44954da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
4496f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                break;
4497f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            }
4498f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
449942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
450042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
450142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                String currentDateString = dateFormat.format(new Date()).toString();
450242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                return db.rawQuery(
450342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    "SELECT" +
450442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " 'vcards_' || ? || '.vcf' AS " + OpenableColumns.DISPLAY_NAME + "," +
450542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " NULL AS " + OpenableColumns.SIZE,
450642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    new String[] { currentDateString });
450742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
450842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
4509ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_FILTER: {
4510916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                String filterParam = "";
4511ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
4512916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    filterParam = uri.getLastPathSegment();
4513ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
45147ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov                setTablesAndProjectionMapForContactsWithSnippet(
45157ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov                        qb, uri, projection, filterParam, directoryId);
45164b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                profileRestrictionColumnName = Contacts.IS_USER_PROFILE;
45176ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                sortOrder = prependProfileSortIfNeeded(uri, sortOrder, suppressProfileCheck);
4518ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4519ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4520ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
4521ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT_FILTER:
4522ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT: {
45232f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Basically the resultant SQL should look like this:
45242f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // (SQL for listing starred items)
45252f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // UNION ALL
45262f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // (SQL for listing frequently contacted items)
45272f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // ORDER BY ...
45282f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
45292f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final boolean phoneOnly = readBooleanQueryParameter(
45302f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        uri, ContactsContract.STREQUENT_PHONE_ONLY, false);
45312f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (match == CONTACTS_STREQUENT_FILTER && uri.getPathSegments().size() > 3) {
45324a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
45334a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
4534e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
45355e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
45362f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    selection = DbQueryUtils.concatenateClauses(selection, sb.toString());
45374a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
45384a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
45392f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                String[] subProjection = null;
45405e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                if (projection != null) {
45412f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    subProjection = appendProjectionArg(projection, TIMES_USED_SORT_COLUMN);
45425e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                }
45435e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
45444a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                // Build the first query for starred
45454928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                setTablesAndProjectionMapForContacts(qb, uri, projection, false);
45464928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                qb.setProjectionMap(phoneOnly ?
45474928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                        sStrequentPhoneOnlyStarredProjectionMap
45484928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                        : sStrequentStarredProjectionMap);
45492f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                qb.appendWhere(DbQueryUtils.concatenateClauses(
45502f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        selection, Contacts.IS_USER_PROFILE + "=0"));
45512f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                qb.setStrict(true);
45522f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final String starredQuery = qb.buildQuery(subProjection,
455324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        Contacts.STARRED + "=1", Contacts._ID, null, null, null);
4554d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
45552f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Reset the builder.
4556d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
45572f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                qb.setStrict(true);
45584928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
45594928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                // Build the second query for frequent part.
45604928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                final String frequentQuery;
45614928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                if (phoneOnly) {
45624928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    final StringBuilder tableBuilder = new StringBuilder();
45634928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // In phone only mode, we need to look at view_data instead of
45644928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // contacts/raw_contacts to obtain actual phone numbers. One problem is that
45654928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // view_data is much larger than view_contacts, so our query might become much
45664928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // slower.
45674928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    //
45684928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // To avoid the possible slow down, we start from data usage table and join
45694928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // view_data to the table, assuming data usage table is quite smaller than
45704928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // data rows (almost always it should be), and we don't want any phone
45714928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // numbers not used by the user. This way sqlite is able to drop a number of
45724928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // rows in view_data in the early stage of data lookup.
45734928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    tableBuilder.append(Tables.DATA_USAGE_STAT
45744928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + " INNER JOIN " + Views.DATA + " " + Tables.DATA
45754928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "="
45764928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                                + DataColumns.CONCRETE_ID + " AND "
45774928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "="
45784928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                                + DataUsageStatColumns.USAGE_TYPE_INT_CALL + ")");
45794928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    appendContactPresenceJoin(tableBuilder, projection, RawContacts.CONTACT_ID);
45804928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    appendContactStatusUpdateJoin(tableBuilder, projection,
45814928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            ContactsColumns.LAST_STATUS_UPDATE_ID);
45824928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
45834928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setTables(tableBuilder.toString());
45844928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setProjectionMap(sStrequentPhoneOnlyFrequentProjectionMap);
45854928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.appendWhere(DbQueryUtils.concatenateClauses(
45864928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            selection,
45874928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            Contacts.STARRED + "=0 OR " + Contacts.STARRED + " IS NULL",
45884928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            MimetypesColumns.MIMETYPE + " IN ("
45894928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + "'" + Phone.CONTENT_ITEM_TYPE + "', "
45904928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + "'" + SipAddress.CONTENT_ITEM_TYPE + "')"));
45914928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    frequentQuery = qb.buildQuery(subProjection, null, null, null, null, null);
45924928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                } else {
45934928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    setTablesAndProjectionMapForContacts(qb, uri, projection, true);
45944928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setProjectionMap(sStrequentFrequentProjectionMap);
45954928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.appendWhere(DbQueryUtils.concatenateClauses(
45964928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            selection,
45974928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            "(" + Contacts.STARRED + " =0 OR " + Contacts.STARRED + " IS NULL)",
45984928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            Contacts.IS_USER_PROFILE + "=0"));
45994928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    frequentQuery = qb.buildQuery(subProjection,
46004928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            null, Contacts._ID, null, null, null);
46014928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                }
4602d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
4603d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
46042f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final String unionQuery =
46052f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
46062f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                                STREQUENT_ORDER_BY, STREQUENT_LIMIT);
46072f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
46082f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Here, we need to use selection / selectionArgs (supplied from users) "twice",
46092f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // as we want them both for starred items and for frequently contacted items.
46102f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                //
46112f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // e.g. if the user specify selection = "starred =?" and selectionArgs = "0",
46122f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // the resultant SQL should be like:
46132f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // SELECT ... WHERE starred =? AND ...
46142f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // UNION ALL
46152f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // SELECT ... WHERE starred =? AND ...
46162f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                String[] doubledSelectionArgs = null;
46172f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (selectionArgs != null) {
46182f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    final int length = selectionArgs.length;
46192f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    doubledSelectionArgs = new String[length * 2];
46207d7d0e95636344c01eb4e4d034791c199bee98e9Daisuke Miyakawa                    System.arraycopy(selectionArgs, 0, doubledSelectionArgs, 0, length);
46217d7d0e95636344c01eb4e4d034791c199bee98e9Daisuke Miyakawa                    System.arraycopy(selectionArgs, 0, doubledSelectionArgs, length, length);
46222f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                }
46232f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
46242f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                Cursor cursor = db.rawQuery(unionQuery, doubledSelectionArgs);
46252f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (cursor != null) {
46262f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    cursor.setNotificationUri(getContext().getContentResolver(),
4627d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
4628d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
46292f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                return cursor;
4630d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
4631d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
463245ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa            case CONTACTS_FREQUENT: {
463345ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                setTablesAndProjectionMapForContacts(qb, uri, projection, true);
463445ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                qb.setProjectionMap(sStrequentFrequentProjectionMap);
4635363bdaba2994539e1a3a2342a9fcf223604d69eaDaisuke Miyakawa                qb.appendWhere(Contacts.IS_USER_PROFILE + "=0");
463645ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                groupBy = Contacts._ID;
463745ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                if (!TextUtils.isEmpty(sortOrder)) {
463845ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                    sortOrder = FREQUENT_ORDER_BY + ", " + sortOrder;
463945ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                } else {
464045ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                    sortOrder = FREQUENT_ORDER_BY;
464145ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                }
464245ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                break;
464345ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa            }
464445ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa
4645ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_GROUP: {
4646763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
4647b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
464871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
46494a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
4650b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
4651b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
4652b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
4653b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
465424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
465524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
465624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForContacts(qb, uri, projection);
465724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(Contacts.IS_USER_PROFILE + "=1");
465824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
465924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
466024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
466124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_ENTITIES: {
466224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
466324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForEntities(qb, uri, projection);
466424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + Contacts.IS_USER_PROFILE + "=1");
466524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
466624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
466724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
466824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA: {
466924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
467024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
467124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1");
467224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
467324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
467424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
467524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA_ID: {
467624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
467724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
467824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
467924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + Data._ID + "=? AND "
468024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1");
468124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
468224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
468324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
468424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD: {
468524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
4686ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
468724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.setProjectionMap(sContactsVCardProjectionMap);
468824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(Contacts.IS_USER_PROFILE + "=1");
468924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
469024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
469124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
4692a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_DATA: {
46934a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
469482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
46954da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
46964da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
46976bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
46986bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
469900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
4700a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
47013653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
4702afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
470382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
47044da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
47054da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
47063653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
47073653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                break;
47083653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
47093653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
4710a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_ENTITIES: {
4711a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
4712a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
4713a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
4714a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
4715a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
4716a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
4717a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4718a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ENTITIES:
4719a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_ENTITIES: {
4720a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
4721a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                int segmentCount = pathSegments.size();
4722a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount < 4) {
4723a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
4724a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            "Missing a lookup key", uri));
4725a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
4726a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
4727a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount == 5) {
4728a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
4729a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
4730a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    setTablesAndProjectionMapForEntities(lookupQb, uri, projection);
4731a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
4732a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4733a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
4734a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4735a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.CONTACT_ID, contactId,
4736a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.LOOKUP_KEY, lookupKey);
4737a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
4738a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        return c;
4739a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    }
4740a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
4741a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4742a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
4743a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
4744a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
4745a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + Contacts.Entity.CONTACT_ID + "=?");
4746a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
4747a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
4748a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
47493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
47503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
47513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
47523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
47533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
47543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
47553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
47563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
47573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItemsColumns.CONCRETE_ID + "=?");
47583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
47593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
47603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
47613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_LIMIT: {
47626802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                MatrixCursor cursor = new MatrixCursor(new String[]{StreamItems.MAX_ITEMS}, 1);
47636802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                cursor.addRow(new Object[]{MAX_STREAM_ITEMS_PER_RAW_CONTACT});
47643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return cursor;
47653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
47663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
47673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
47683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
47693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
47703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
47713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
47723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
47733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
47743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
47753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemId);
47763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=?");
47773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
47783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
47793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
47803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
47813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
47823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
47833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
47843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemPhotoId);
47853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemId);
47863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=? AND " +
47873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=?");
47883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
47893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
47903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
4791f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case PHOTO_DIMENSIONS: {
4792f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                MatrixCursor cursor = new MatrixCursor(
4793f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{DisplayPhoto.DISPLAY_MAX_DIM, DisplayPhoto.THUMBNAIL_MAX_DIM},
4794f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        1);
4795f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                cursor.addRow(new Object[]{mMaxDisplayPhotoDim, mMaxThumbnailPhotoDim});
4796f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                return cursor;
4797f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
4798f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
47994a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case PHONES: {
480082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
480189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
48022815f58f72f109790585931f601a63ddc02536a5Evan Millar                break;
48032815f58f72f109790585931f601a63ddc02536a5Evan Millar            }
48042815f58f72f109790585931f601a63ddc02536a5Evan Millar
480548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID: {
480682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
48074da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
480848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
48094da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
481048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
481148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
481248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
4813ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
481446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
481546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                Integer typeInt = sDataUsageTypeMap.get(typeParam);
481646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (typeInt == null) {
481746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    typeInt = DataUsageStatColumns.USAGE_TYPE_INT_CALL;
481846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
481946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
482089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
4821ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
48224a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
48234a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
4824a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    sb.append(" AND (");
48255e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
482645d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    boolean hasCondition = false;
48275e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    boolean orNeeded = false;
48285e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String normalizedName = NameNormalizer.normalize(filterParam);
48295e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (normalizedName.length() > 0) {
4830155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN " +
4831155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
4832155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.SEARCH_INDEX +
4833155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " JOIN " + Tables.RAW_CONTACTS +
4834155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
4835155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
4836155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE " + SearchIndexColumns.NAME + " MATCH ");
48372352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                        DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filterParam) + "*");
4838155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(")");
48395e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        orNeeded = true;
484045d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
48415e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
48425e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
4843892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    String number = PhoneNumberUtils.normalizeNumber(filterParam);
4844892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (!TextUtils.isEmpty(number)) {
48455e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        if (orNeeded) {
48465e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                            sb.append(" OR ");
48475e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        }
48485e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data._ID +
4849892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                " IN (SELECT DISTINCT " + PhoneLookupColumns.DATA_ID
4850892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " FROM " + Tables.PHONE_LOOKUP
4851892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
4852892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append(number);
4853892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append("%')");
485445d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
485545d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    }
485645d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov
485745d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    if (!hasCondition) {
485845d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // If it is neither a phone number nor a name, the query should return
485945d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // an empty cursor.  Let's ensure that.
486045d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        sb.append("0");
48615e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
48625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
4863a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
4864ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
48655e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = PhoneColumns.NORMALIZED_NUMBER + "," + RawContacts.CONTACT_ID;
4866a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
486746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
486846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
486946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = accountPromotionSortOrder + ", " + PHONE_FILTER_SORT_ORDER;
487046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    } else {
487146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = PHONE_FILTER_SORT_ORDER;
487246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    }
4873a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
4874ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4875ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4876ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
48774a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case EMAILS: {
487882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
487989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
48804a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                break;
48814a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
48824a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
488348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID: {
488482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
48854da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
48864da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'"
48874da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        + " AND " + Data._ID + "=?");
488848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
488948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
489048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
48915e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_LOOKUP: {
489282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
489389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
48944a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
489508768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String email = uri.getLastPathSegment();
489608768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String address = mDbHelper.extractAddressFromEmailAddress(email);
489708768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, address);
489808768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    qb.appendWhere(" AND UPPER(" + Email.DATA + ")=UPPER(?)");
48994a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
4900ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4901ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4902ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
49035e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_FILTER: {
490446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
490546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                Integer typeInt = sDataUsageTypeMap.get(typeParam);
490646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (typeInt == null) {
490746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    typeInt = DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT;
490846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
490946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
491007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                String filterParam = null;
49117d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa
491207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
491307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    filterParam = uri.getLastPathSegment();
491407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    if (TextUtils.isEmpty(filterParam)) {
491507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                        filterParam = null;
491607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    }
491707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                }
49185e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
491907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (filterParam == null) {
492007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    // If the filter is unspecified, return nothing
492107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    qb.appendWhere(" AND 0");
492207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                } else {
492307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    StringBuilder sb = new StringBuilder();
492407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(" AND " + Data._ID + " IN (");
492507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(
492607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            "SELECT " + Data._ID +
492707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " FROM " + Tables.DATA +
49282a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                            " WHERE " + DataColumns.MIMETYPE_ID + "=");
49292a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                    sb.append(mDbHelper.getMimeTypeIdForEmail());
49302a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                    sb.append(" AND " + Data.DATA1 + " LIKE ");
493107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(sb, filterParam + '%');
493220938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                    if (!filterParam.contains("@")) {
4933155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(
4934155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " UNION SELECT " + Data._ID +
4935155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.DATA +
4936155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE +" + DataColumns.MIMETYPE_ID + "=");
4937155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(mDbHelper.getMimeTypeIdForEmail());
4938155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(" AND " + Data.RAW_CONTACT_ID + " IN " +
4939155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
4940155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.SEARCH_INDEX +
4941155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " JOIN " + Tables.RAW_CONTACTS +
4942155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
4943155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
4944155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE " + SearchIndexColumns.NAME + " MATCH ");
49452352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                        DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filterParam) + "*");
4946155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(")");
49475e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
49485e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
4949a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
49505e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                }
49515e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
4952a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
495346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
495446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
495546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = accountPromotionSortOrder + ", " + EMAIL_FILTER_SORT_ORDER;
49567d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                    } else {
49577d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                        sortOrder = EMAIL_FILTER_SORT_ORDER;
49587d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                    }
4959a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
49605e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                break;
49615e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
49625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
4963ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
496482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
496589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
496689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
4967ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4968ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4969ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
497048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
497182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
49724da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
497348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
497448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
49754da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
497648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
497748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
497848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
49795ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
4980763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
49814b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                profileRestrictionColumnName = RawContacts.RAW_CONTACT_IS_USER_PROFILE;
49824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
49834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
49844f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
49855ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
49865ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
4987afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForRawContact(db, rawContactId, false);
4988763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
49894da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
49904da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
49914f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
49924f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
49934f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
49945ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
49955ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
499682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
49974da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
49984da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=?");
49994b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                profileRestrictionColumnName = RawContacts.RAW_CONTACT_IS_USER_PROFILE;
500024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
500124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
500224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
50033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case RAW_CONTACTS_ID_STREAM_ITEMS: {
50043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
5005afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForRawContact(db, rawContactId, false);
50063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
50073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
50083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItems.RAW_CONTACT_ID + "=?");
50093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
50103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
501124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
501224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS: {
501324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
501424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForRawContacts(qb, uri);
501524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1");
501624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
501724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
501824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
501924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID: {
502024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
502124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = ContentUris.parseId(uri);
502224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
502324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForRawContacts(qb, uri);
502424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1 AND "
502524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        + RawContacts._ID + "=?");
502624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
502724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
502824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
502924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID_DATA: {
503024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
503124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(2));
503224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
503324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
503424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1 AND "
503524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        + Data.RAW_CONTACT_ID + "=?");
503624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
503724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
503824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
503924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID_ENTITIES: {
504024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
504124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(2));
504224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
504324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForRawEntities(qb, uri);
504424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1 AND "
504524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        + RawContacts._ID + "=?");
5046e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
5047e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
5048e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
5049e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            case DATA: {
505082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
50514b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                profileRestrictionColumnName = RawContacts.RAW_CONTACT_IS_USER_PROFILE;
5052e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
5053e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
5054e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
50554f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case DATA_ID: {
505624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long dataId = ContentUris.parseId(uri);
5057afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForData(db, dataId, false);
505882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
50594da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
50604da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
5061a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                break;
5062a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            }
5063a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov
5064a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
50654a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
5066a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
5067a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
5068a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
5069892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    sortOrder = " length(lookup.normalized_number) DESC";
5070a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
5071a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
5072e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
5073e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                String numberE164 = PhoneNumberUtils.formatNumberToE164(number,
5074e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                        mDbHelper.getCurrentCountryIso());
5075892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                String normalizedNumber =
5076892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        PhoneNumberUtils.normalizeNumber(number);
5077892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                mDbHelper.buildPhoneLookupAndContactQuery(qb, normalizedNumber, numberE164);
5078e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                qb.setProjectionMap(sPhoneLookupProjectionMap);
5079e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                // Phone lookup cannot be combined with a selection
5080e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selection = null;
5081e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selectionArgs = null;
5082a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
5083a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
5084a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
5085ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
5086ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.GROUPS);
5087ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
508889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
5089ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5090ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5091ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5092ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
5093ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.GROUPS);
5094ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
50954da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
50964da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Groups._ID + "=?");
5097ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5098ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5099ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5100ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
5101f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                final boolean returnGroupCountPerAccount =
5102f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        readBooleanQueryParameter(uri, Groups.PARAM_RETURN_GROUP_COUNT_PER_ACCOUNT,
5103f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                                false);
5104f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                qb.setTables(Views.GROUPS + " AS " + Tables.GROUPS);
5105f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                qb.setProjectionMap(returnGroupCountPerAccount ?
5106f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        sGroupsSummaryProjectionMapWithGroupCountPerAccount
5107f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        : sGroupsSummaryProjectionMap);
510889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
5109f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                groupBy = GroupsColumns.CONCRETE_ID;
5110ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5111ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5112ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5113b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
51140c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
5115b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
5116b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
5117b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
5118b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
511931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
5120d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
51212d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                String filter = null;
51222d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
51232d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                    filter = uri.getPathSegments().get(3);
51242d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                }
512531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
5126d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                if (limit != null) {
5127d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(limit);
512831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
512931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
513031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
513131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
51325b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                ArrayList<AggregationSuggestionParameter> parameters = null;
51335b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                List<String> query = uri.getQueryParameters("query");
51345b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                if (query != null && !query.isEmpty()) {
51355b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    parameters = new ArrayList<AggregationSuggestionParameter>(query.size());
51365b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    for (String parameter : query) {
51375b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        int offset = parameter.indexOf(':');
51385b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        parameters.add(offset == -1
51395b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                ? new AggregationSuggestionParameter(
514076dfa406e2cde19c824983c37fc92c1c5bf63eecDaniel Lehmann                                        AggregationSuggestions.PARAMETER_MATCH_NAME,
51415b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter)
51425b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                : new AggregationSuggestionParameter(
51435b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(0, offset),
51445b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(offset + 1)));
51455b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    }
51465b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                }
51475b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
5148763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
51497581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov
51507581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov                return mContactAggregator.queryAggregationSuggestions(qb, projection, contactId,
51515b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        maxSuggestions, filter, parameters);
515231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
515331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
5154eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
5155eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setTables(Tables.SETTINGS);
5156eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setProjectionMap(sSettingsProjectionMap);
515789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
5158e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
5159e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // When requesting specific columns, this query requires
5160e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // late-binding of the GroupMembership MIME-type.
5161b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                final String groupMembershipMimetypeId = Long.toString(mDbHelper
5162e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
516382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
5164b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_COUNT)) {
5165e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
5166e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
516782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
5168b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_WITH_PHONES)) {
5169e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
5170e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
5171e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
5172eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
5173eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
5174eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
517582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
51760a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
51775ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
51785ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
51795ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
518082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES_ID: {
51810a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
51824da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
51834da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(DataColumns.CONCRETE_ID + "=?");
51845ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
51855ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
51865ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
5187c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS: {
5188174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(
5189174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        db, uri, projection, limit);
5190c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5191c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
5192c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT: {
51932d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                String lookupKey = uri.getLastPathSegment();
5194174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                String filter = getQueryParameter(
5195174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        uri, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
5196174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchShortcutRefresh(
5197174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        db, projection, lookupKey, filter);
5198c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5199c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
52001b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS:
5201ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
52021b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
52031b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
52041b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
52051b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_WITH_PHONES:
5206ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
52071b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
52081b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.HAS_PHONE_NUMBER + "=1");
52091b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
52101b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
52111b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_FAVORITES:
5212ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
52131b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
52141b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.STARRED + "=1");
52151b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
52161b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
52171b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_GROUP_NAME:
5218ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
52191b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
522071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
52211b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
52221b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
52231b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
522446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITIES: {
5225a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
522646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
522746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
522846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
522946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITY_ID: {
523046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
5231a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
52324da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
52334da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
523446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
523546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
523646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
523709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            case PROVIDER_STATUS: {
523809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                return queryProviderStatus(uri, projection);
523909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
524009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5241d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES : {
5242d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
5243d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
5244d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
5245d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
5246d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
5247d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID : {
5248385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                long id = ContentUris.parseId(uri);
5249d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
5250d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
5251385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(id));
5252d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.appendWhere(Directory._ID + "=?");
5253d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
5254d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
5255d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
52567a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            case COMPLETE_NAME: {
52577a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                return completeName(uri, projection);
52587a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
52597a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
52604f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
5261f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
5262c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                        sortOrder, limit);
52634f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
52644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
526509e69522745551522c55dff27424496f255def46Daniel Lehmann        qb.setStrict(true);
52667f786e5cbde9975b9632beb9b6d19eeef8a64cf1Dmitri Plotnikov
52674b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki        if (profileRestrictionColumnName != null) {
52684b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki            // This check is very slow and most of the rows will pass though this check, so
52694b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki            // it should be put after user's selection, so SQLite won't do this check first.
52704b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki            selection = appendProfileRestriction(uri, profileRestrictionColumnName,
52714b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                    suppressProfileCheck, selection);
52724b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki        }
52734b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki
5274ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        Cursor cursor =
5275ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit);
5276ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (readBooleanQueryParameter(uri, ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, false)) {
5277ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            cursor = bundleLetterCountExtras(cursor, db, qb, selection, selectionArgs, sortOrder);
5278ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5279ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        return cursor;
52805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
52815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
52825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
52835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String selection, String[] selectionArgs, String sortOrder, String groupBy,
52845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String limit) {
5285038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        if (projection != null && projection.length == 1
5286038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                && BaseColumns._COUNT.equals(projection[0])) {
5287038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            qb.setProjectionMap(sCountProjectionMap);
5288038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
52895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
52905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sortOrder, limit);
52914f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
52924f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
52934f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
52944f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
52954f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
52964f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
529709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    /**
529809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     * Creates a single-row cursor containing the current status of the provider.
529909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     */
530009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private Cursor queryProviderStatus(Uri uri, String[] projection) {
530109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
530209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        RowBuilder row = cursor.newRow();
530309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
530409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            if (ProviderStatus.STATUS.equals(projection[i])) {
530509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mProviderStatus);
530609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            } else if (ProviderStatus.DATA1.equals(projection[i])) {
530709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mEstimatedStorageRequirement);
530809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
530909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        }
531009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        return cursor;
531109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    }
531209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5313a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /**
5314a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * Runs the query with the supplied contact ID and lookup ID.  If the query succeeds,
5315a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * it returns the resulting cursor, otherwise it returns null and the calling
5316a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * method needs to resolve the lookup key and rerun the query.
5317a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     */
5318a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private Cursor queryWithContactIdAndLookupKey(SQLiteQueryBuilder lookupQb,
5319a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            SQLiteDatabase db, Uri uri,
5320a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection, String selection, String[] selectionArgs,
5321a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String sortOrder, String groupBy, String limit,
5322a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn, long contactId, String lookupKeyColumn, String lookupKey) {
5323a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        String[] args;
5324a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (selectionArgs == null) {
5325a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[2];
5326a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        } else {
5327a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[selectionArgs.length + 2];
5328a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
5329a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5330a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[0] = String.valueOf(contactId);
5331a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[1] = Uri.encode(lookupKey);
5332a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        lookupQb.appendWhere(contactIdColumn + "=? AND " + lookupKeyColumn + "=?");
5333a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        Cursor c = query(db, lookupQb, projection, selection, args, sortOrder,
5334a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                groupBy, limit);
5335a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (c.getCount() != 0) {
5336a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return c;
5337a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5338a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5339a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        c.close();
5340a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return null;
5341a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
534209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5343bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov    private static final class AddressBookIndexQuery {
5344bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String LETTER = "letter";
5345bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String TITLE = "title";
5346bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String COUNT = "count";
5347ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5348bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
5349bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                LETTER, TITLE, COUNT
5350ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        };
5351ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5352bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_LETTER = 0;
5353bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_TITLE = 1;
5354bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_COUNT = 2;
5355bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
535624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // The first letter of the sort key column is what is used for the index headings, except
535724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // in the case of the user's profile, in which case it is empty.
535824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        public static final String SECTION_HEADING_TEMPLATE =
535924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                "(CASE WHEN %1$s=1 THEN '' ELSE SUBSTR(%2$s,1,1) END)";
536024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
5361de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa        public static final String ORDER_BY = LETTER + " COLLATE " + PHONEBOOK_COLLATOR_NAME;
5362ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
5363ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5364ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    /**
5365ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * Computes counts by the address book index titles and adds the resulting tally
5366ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * to the returned cursor as a bundle of extras.
5367ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     */
5368ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    private Cursor bundleLetterCountExtras(Cursor cursor, final SQLiteDatabase db,
5369ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            SQLiteQueryBuilder qb, String selection, String[] selectionArgs, String sortOrder) {
5370ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortKey;
5371ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5372ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // The sort order suffix could be something like "DESC".
5373ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // We want to preserve it in the query even though we will change
5374ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // the sort column itself.
5375ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortOrderSuffix = "";
5376ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (sortOrder != null) {
537724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
537824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // If the sort order contains one of the "is_profile" columns, we need to strip it out
537924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // first.
538024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            if (sortOrder.contains(Contacts.IS_USER_PROFILE)
538124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    || sortOrder.contains(RawContacts.RAW_CONTACT_IS_USER_PROFILE)) {
538224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                String[] splitOrderClauses = sortOrder.split(",");
538324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                StringBuilder rejoinedClause = new StringBuilder();
538424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                for (String orderClause : splitOrderClauses) {
538524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    if (!orderClause.contains(Contacts.IS_USER_PROFILE)
538624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                            && !orderClause.contains(RawContacts.RAW_CONTACT_IS_USER_PROFILE)) {
538724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        if (rejoinedClause.length() > 0) {
538824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                            rejoinedClause.append(", ");
538924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        }
539024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        rejoinedClause.append(orderClause.trim());
539124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    }
539224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                }
539324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                sortOrder = rejoinedClause.toString();
539424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
539524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
5396ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int spaceIndex = sortOrder.indexOf(' ');
5397ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            if (spaceIndex != -1) {
5398ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder.substring(0, spaceIndex);
5399ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortOrderSuffix = sortOrder.substring(spaceIndex);
5400ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            } else {
5401ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder;
5402ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
5403ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } else {
5404ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            sortKey = Contacts.SORT_KEY_PRIMARY;
5405ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5406ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5407bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        String locale = getLocale().toString();
5408ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        HashMap<String, String> projectionMap = Maps.newHashMap();
540924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
541024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // The user profile column varies depending on the view.
5411ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        String profileColumn = qb.getTables().contains(Views.CONTACTS)
541224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                ? Contacts.IS_USER_PROFILE
541324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                : RawContacts.RAW_CONTACT_IS_USER_PROFILE;
541424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        String sectionHeading = String.format(
541524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                AddressBookIndexQuery.SECTION_HEADING_TEMPLATE, profileColumn, sortKey);
5416bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.LETTER,
541724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                sectionHeading + " AS " + AddressBookIndexQuery.LETTER);
5418bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5419bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        /**
5420bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * Use the GET_PHONEBOOK_INDEX function, which is an android extension for SQLite3,
5421bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * to map the first letter of the sort key to a character that is traditionally
5422bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * used in phonebooks to represent that letter.  For example, in Korean it will
5423bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * be the first consonant in the letter; for Japanese it will be Hiragana rather
5424bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * than Katakana.
5425bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         */
5426ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.TITLE,
542724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                "GET_PHONEBOOK_INDEX(" + sectionHeading + ",'" + locale + "')"
5428bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                        + " AS " + AddressBookIndexQuery.TITLE);
5429ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.COUNT,
5430ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                "COUNT(" + Contacts._ID + ") AS " + AddressBookIndexQuery.COUNT);
5431ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        qb.setProjectionMap(projectionMap);
5432ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5433f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov        Cursor indexCursor = qb.query(db, AddressBookIndexQuery.COLUMNS, selection, selectionArgs,
5434ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY, null /* having */,
5435ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY + sortOrderSuffix);
5436ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5437ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        try {
5438f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            int groupCount = indexCursor.getCount();
5439ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            String titles[] = new String[groupCount];
5440ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int counts[] = new int[groupCount];
5441bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            int indexCount = 0;
5442bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            String currentTitle = null;
5443bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5444bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // Since GET_PHONEBOOK_INDEX is a many-to-1 function, we may end up
5445bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // with multiple entries for the same title.  The following code
5446bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // collapses those duplicates.
5447ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            for (int i = 0; i < groupCount; i++) {
5448f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov                indexCursor.moveToNext();
5449bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String title = indexCursor.getString(AddressBookIndexQuery.COLUMN_TITLE);
5450bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int count = indexCursor.getInt(AddressBookIndexQuery.COLUMN_COUNT);
5451bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                if (indexCount == 0 || !TextUtils.equals(title, currentTitle)) {
5452bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    titles[indexCount] = currentTitle = title;
5453bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount] = count;
5454bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    indexCount++;
5455bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                } else {
5456bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount - 1] += count;
5457bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                }
5458bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            }
5459bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5460bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            if (indexCount < groupCount) {
5461bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String[] newTitles = new String[indexCount];
5462bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(titles, 0, newTitles, 0, indexCount);
5463bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                titles = newTitles;
5464bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5465bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int[] newCounts = new int[indexCount];
5466bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(counts, 0, newCounts, 0, indexCount);
5467bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                counts = newCounts;
5468ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
5469ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5470e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return new AddressBookCursor((CrossProcessCursor) cursor, titles, counts);
5471ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } finally {
5472f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            indexCursor.close();
5473ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5474ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
5475ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
54762d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    /**
547792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Returns the contact Id for the contact identified by the lookupKey.
547892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Robust against changes in the lookup key: if the key has changed, will
547992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * look up the contact by the raw contact IDs or name encoded in the lookup
548092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * key.
54812d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     */
54822d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    public long lookupContactIdByLookupKey(SQLiteDatabase db, String lookupKey) {
54835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactLookupKey key = new ContactLookupKey();
54845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ArrayList<LookupKeySegment> segments = key.parse(lookupKey);
54855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
548692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        long contactId = -1;
548792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_SOURCE_ID)) {
548892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdBySourceIds(db, segments);
548992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
549092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
549192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
549292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
549392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
549492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        boolean hasRawContactIds =
549592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID);
549692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds) {
549792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdByRawContactIds(db, segments);
549892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
549992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
550092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
550192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
550292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
550392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds
550492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                || lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME)) {
55055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            contactId = lookupContactIdByDisplayNames(db, segments);
55065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
55075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
55085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return contactId;
55095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
55105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
55115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupBySourceIdQuery {
55125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
55135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
55145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
55155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
55165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
55175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
55185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.SOURCE_ID
55195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
55205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
55215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
55225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
55235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
55245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int SOURCE_ID = 3;
55255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
55265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
55275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdBySourceIds(SQLiteDatabase db,
55285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
55295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
55305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(RawContacts.SOURCE_ID + " IN (");
55315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
55325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
553392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID) {
55345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
55355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
55365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
55375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
55385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
55395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
55405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
55415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupBySourceIdQuery.TABLE, LookupBySourceIdQuery.COLUMNS,
55425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
55435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
55445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
55455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE);
55465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME);
55475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
55485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
55495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID);
55505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
55515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
555292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID
555392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
55545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(sourceId)) {
55555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupBySourceIdQuery.CONTACT_ID);
55565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
55575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
55585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
55595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
55605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
55615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
55625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
55635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
55645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
55655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
55665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
556792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByRawContactIdQuery {
556892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
55695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
55705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
55715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
55725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
55735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
557492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts._ID,
55755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
55765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
55775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
55785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
55795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
558092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ID = 3;
55815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
55825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
558392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByRawContactIds(SQLiteDatabase db,
558492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            ArrayList<LookupKeySegment> segments) {
558592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
558692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(RawContacts._ID + " IN (");
55875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
55885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
558992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
559092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(segment.rawContactId);
559192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(",");
55925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
55935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
559492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
559592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
55965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
559792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        Cursor c = db.query(LookupByRawContactIdQuery.TABLE, LookupByRawContactIdQuery.COLUMNS,
559892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                 sb.toString(), null, null, null, null);
559992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        try {
560092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            while (c.moveToNext()) {
560192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountType = c.getString(LookupByRawContactIdQuery.ACCOUNT_TYPE);
560292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountName = c.getString(LookupByRawContactIdQuery.ACCOUNT_NAME);
560392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                int accountHashCode =
560492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
560592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String rawContactId = c.getString(LookupByRawContactIdQuery.ID);
560692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
560792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
560892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID
560992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
561092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && segment.rawContactId.equals(rawContactId)) {
561192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        segment.contactId = c.getLong(LookupByRawContactIdQuery.CONTACT_ID);
561292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        break;
561392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    }
561492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                }
561592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
561692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        } finally {
561792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            c.close();
56185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
56195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
562092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return getMostReferencedContactId(segments);
562192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
562292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
562392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByDisplayNameQuery {
562492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
562592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
562692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String COLUMNS[] = {
562792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.CONTACT_ID,
562892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
562992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
563092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME
563192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        };
563292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
563392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int CONTACT_ID = 0;
563492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_TYPE = 1;
563592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_NAME = 2;
563692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int NORMALIZED_NAME = 3;
563792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
563892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
563992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByDisplayNames(SQLiteDatabase db,
564092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
56415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
56425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(NameLookupColumns.NORMALIZED_NAME + " IN (");
56435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
56445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
564592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
564692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
56475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
56485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
56495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
56505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
56515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
56525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
56535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                + " AND " + RawContacts.CONTACT_ID + " NOT NULL");
56545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
56555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupByDisplayNameQuery.TABLE, LookupByDisplayNameQuery.COLUMNS,
56565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
56575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
56585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
56595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE);
56605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME);
56615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
56625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
56635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME);
56645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
56655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
566692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if ((segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
566792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID)
566892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
56695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(name)) {
56705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupByDisplayNameQuery.CONTACT_ID);
56715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
56725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
56735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
56745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
56755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
56765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
56775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
56785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
56795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
56805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
56815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
568292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private boolean lookupKeyContainsType(ArrayList<LookupKeySegment> segments, int lookupType) {
568392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
568492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            LookupKeySegment segment = segments.get(i);
568592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == lookupType) {
568692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return true;
568792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
568892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
568992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
569092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return false;
569192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
569292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
5693ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateLookupKeyForRawContact(SQLiteDatabase db, long rawContactId) {
5694ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov        mContactAggregator.updateLookupKeyForRawContact(db, rawContactId);
5695ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    }
5696ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov
56975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /**
56985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     * Returns the contact ID that is mentioned the highest number of times.
56995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     */
57005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long getMostReferencedContactId(ArrayList<LookupKeySegment> segments) {
57015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Collections.sort(segments);
57025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
57035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long bestContactId = -1;
57045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int bestRefCount = 0;
57055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
57065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = -1;
57075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int count = 0;
57085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
57095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int segmentCount = segments.size();
57105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segmentCount; i++) {
57115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
57125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.contactId != -1) {
57135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segment.contactId == contactId) {
57145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count++;
57155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                } else {
57165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (count > bestRefCount) {
57175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestContactId = contactId;
57185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestRefCount = count;
57195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
57205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    contactId = segment.contactId;
57215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count = 1;
57225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
57235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
57245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
57255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count > bestRefCount) {
57265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return contactId;
57275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
57285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return bestContactId;
57295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
57305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
57315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
5732763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
5733763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            String[] projection) {
57344928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa        setTablesAndProjectionMapForContacts(qb, uri, projection, false);
57352f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    }
57362f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
57372f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    /**
57384928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * @param includeDataUsageStat true when the table should include DataUsageStat table.
57394928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * Note that this uses INNER JOIN instead of LEFT OUTER JOIN, so some of data in Contacts
57404928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * may be dropped.
57412f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     */
57422f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
57434928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            String[] projection, boolean includeDataUsageStat) {
574482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5745ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.CONTACTS);
57462f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
57472f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa        // Just for frequently contacted contacts in Strequent Uri handling.
57484928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa        if (includeDataUsageStat) {
57492f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            sb.append(" INNER JOIN " +
5750ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                    Views.DATA_USAGE_STAT + " AS " + Tables.DATA_USAGE_STAT +
57512f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    " ON (" +
57522f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    DbQueryUtils.concatenateClauses(
57532f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                            DataUsageStatColumns.CONCRETE_TIMES_USED + " > 0",
57544928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            RawContacts.CONTACT_ID + "=" + Views.CONTACTS + "." + Contacts._ID) +
57552f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    ")");
57562f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa        }
57572f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
57587ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
57597ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
5760916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setTables(sb.toString());
5761916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionMap);
5762916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    }
5763916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5764916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /**
5765916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * Finds name lookup records matching the supplied filter, picks one arbitrary match per
5766916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * contact and joins that with other contacts tables.
5767916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     */
5768916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private void setTablesAndProjectionMapForContactsWithSnippet(SQLiteQueryBuilder qb, Uri uri,
57697ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov            String[] projection, String filter, long directoryId) {
57707ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov
57717ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
5772ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.CONTACTS);
5773916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
577403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        if (filter != null) {
577503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            filter = filter.trim();
577603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        }
577703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
577830cc766756461da8d53933f88ea01dd2272a90ebDmitri Plotnikov        if (TextUtils.isEmpty(filter) || (directoryId != -1 && directoryId != Directory.DEFAULT)) {
577930cc766756461da8d53933f88ea01dd2272a90ebDmitri Plotnikov            sb.append(" JOIN (SELECT NULL AS " + SearchSnippetColumns.SNIPPET + " WHERE 0)");
57805e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        } else {
57815e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            appendSearchIndexJoin(sb, uri, projection, filter);
57825e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        }
57837ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
57847ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
578503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        qb.setTables(sb.toString());
578603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionWithSnippetMap);
578703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    }
5788916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
578903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private void appendSearchIndexJoin(
579003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            StringBuilder sb, Uri uri, String[] projection, String filter) {
5791916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5792174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET)) {
579303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String[] args = null;
579403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String snippetArgs =
579503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                    getQueryParameter(uri, SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY);
579603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            if (snippetArgs != null) {
579703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                args = snippetArgs.split(",");
579803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
579903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
58005e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String startMatch = args != null && args.length > 0 ? args[0]
58015e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_START_MATCH;
58025e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String endMatch = args != null && args.length > 1 ? args[1]
58035e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_END_MATCH;
58045e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String ellipsis = args != null && args.length > 2 ? args[2]
58055e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_ELLIPSIS;
58065e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            int maxTokens = args != null && args.length > 3 ? Integer.parseInt(args[3])
58075e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_MAX_TOKENS;
58085e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
5809174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            appendSearchIndexJoin(
5810174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                    sb, filter, true, startMatch, endMatch, ellipsis, maxTokens);
5811174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        } else {
5812174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            appendSearchIndexJoin(sb, filter, false, null, null, null, 0);
5813174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
5814174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    }
5815174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
5816174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    public void appendSearchIndexJoin(StringBuilder sb, String filter,
5817174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            boolean snippetNeeded, String startMatch, String endMatch, String ellipsis,
5818174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            int maxTokens) {
5819174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        boolean isEmailAddress = false;
5820174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String emailAddress = null;
5821174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        boolean isPhoneNumber = false;
5822174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String phoneNumber = null;
5823174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String numberE164 = null;
5824174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
58253716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // If the query consists of a single word, we can do snippetizing after-the-fact for a
58263716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // performance boost.
58273716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        boolean singleTokenSearch = filter.split(QUERY_TOKENIZER_REGEX).length == 1;
58283716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
5829174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (filter.indexOf('@') != -1) {
5830174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            emailAddress = mDbHelper.extractAddressFromEmailAddress(filter);
5831174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            isEmailAddress = !TextUtils.isEmpty(emailAddress);
5832174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        } else {
5833174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            isPhoneNumber = isPhoneNumber(filter);
583404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            if (isPhoneNumber) {
583504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                phoneNumber = PhoneNumberUtils.normalizeNumber(filter);
583604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                numberE164 = PhoneNumberUtils.formatNumberToE164(phoneNumber,
583704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                        mDbHelper.getCountryIso());
583804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            }
5839174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
5840174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
5841174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        sb.append(" JOIN (SELECT " + SearchIndexColumns.CONTACT_ID + " AS snippet_contact_id");
5842174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (snippetNeeded) {
58435e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            sb.append(", ");
58445e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            if (isEmailAddress) {
58453d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append("ifnull(");
58465e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, startMatch);
584704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("||(SELECT MIN(" + Email.ADDRESS + ")");
584804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" FROM " + Tables.DATA_JOIN_RAW_CONTACTS);
584904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" WHERE  " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
585004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + RawContacts.CONTACT_ID + " AND " + Email.ADDRESS + " LIKE ");
585104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                DatabaseUtils.appendEscapedSQLString(sb, filter + "%");
585204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(")||");
58533d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, endMatch);
58543d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append(",");
58553716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
58563716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                // Optimization for single-token search.
58573716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                if (singleTokenSearch) {
58583716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    sb.append(SearchIndexColumns.CONTENT);
58593716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                } else {
58603716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
58613716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                }
58623d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append(")");
58633d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov            } else if (isPhoneNumber) {
58643d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append("ifnull(");
58653d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, startMatch);
586604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("||(SELECT MIN(" + Phone.NUMBER + ")");
586704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" FROM " +
586804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                        Tables.DATA_JOIN_RAW_CONTACTS + " JOIN " + Tables.PHONE_LOOKUP);
586904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" ON " + DataColumns.CONCRETE_ID);
587004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + Tables.PHONE_LOOKUP + "." + PhoneLookupColumns.DATA_ID);
587104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" WHERE  " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
587204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + RawContacts.CONTACT_ID);
587304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" AND " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
587404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(phoneNumber);
587504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("%'");
587604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                if (!TextUtils.isEmpty(numberE164)) {
587704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append(" OR " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
587804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append(numberE164);
587904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append("%'");
588004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                }
588104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(")||");
58825e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, endMatch);
58835e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                sb.append(",");
58843716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
58853716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                // Optimization for single-token search.
58863716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                if (singleTokenSearch) {
58873716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    sb.append(SearchIndexColumns.CONTENT);
58883716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                } else {
58893716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
58903716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                }
58915e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                sb.append(")");
589203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            } else {
589304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                final String normalizedFilter = NameNormalizer.normalize(filter);
589404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                if (!TextUtils.isEmpty(normalizedFilter)) {
58953716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    // Optimization for single-token search.
58963716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    if (singleTokenSearch) {
58973716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(SearchIndexColumns.CONTENT);
58983716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    } else {
58993716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("(CASE WHEN EXISTS (SELECT 1 FROM ");
59003716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.RAW_CONTACTS + " AS rc INNER JOIN ");
59013716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.NAME_LOOKUP + " AS nl ON (rc." + RawContacts._ID);
59023716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("=nl." + NameLookupColumns.RAW_CONTACT_ID);
59033716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(") WHERE nl." + NameLookupColumns.NORMALIZED_NAME);
59043716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(" GLOB '" + normalizedFilter + "*' AND ");
59053716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("nl." + NameLookupColumns.NAME_TYPE + "=");
59063716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(NameLookupType.NAME_COLLATION_KEY + " AND ");
59073716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
59083716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("=rc." + RawContacts.CONTACT_ID);
59093716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(") THEN NULL ELSE ");
59103716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
59113716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(" END)");
59123716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    }
591304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                } else {
591404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append("NULL");
591504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                }
591603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
59175e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            sb.append(" AS " + SearchSnippetColumns.SNIPPET);
59185e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        }
591903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
59205e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(" FROM " + Tables.SEARCH_INDEX);
59215e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(" WHERE ");
59225e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(Tables.SEARCH_INDEX + " MATCH ");
59235e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        if (isEmailAddress) {
59242352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb, "\"" + sanitizeMatch(filter) + "*\"");
59253d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov        } else if (isPhoneNumber) {
59262352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb,
592704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    "\"" + sanitizeMatch(filter) + "*\" OR \"" + phoneNumber + "*\""
59282352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                            + (numberE164 != null ? " OR \"" + numberE164 + "\"" : ""));
592903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        } else {
59302352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filter) + "*");
59319c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        }
593203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        sb.append(") ON (" + Contacts._ID + "=snippet_contact_id)");
5933a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov    }
5934a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov
59352352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov    private String sanitizeMatch(String filter) {
59362352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov        // TODO more robust preprocessing of match expressions
59372352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov        return filter.replace('-', ' ').replace('\"', ' ');
59382352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov    }
59392352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov
59405e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov    private void appendSnippetFunction(
59415e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            StringBuilder sb, String startMatch, String endMatch, String ellipsis, int maxTokens) {
59425e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append("snippet(" + Tables.SEARCH_INDEX + ",");
59435e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, startMatch);
59445e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",");
59455e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, endMatch);
59465e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",");
59475e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, ellipsis);
59485e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
59495e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        // The index of the column used for the snippet, "content"
59505e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",1,");
59515e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(maxTokens);
59525e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(")");
59535e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov    }
59545e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
5955763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForRawContacts(SQLiteQueryBuilder qb, Uri uri) {
5956763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        StringBuilder sb = new StringBuilder();
5957ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.RAW_CONTACTS);
5958763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setTables(sb.toString());
5959763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setProjectionMap(sRawContactsProjectionMap);
5960763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        appendAccountFromParameter(qb, uri);
5961763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    }
5962763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
5963a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForRawEntities(SQLiteQueryBuilder qb, Uri uri) {
5964ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        qb.setTables(Views.RAW_ENTITIES);
5965a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sRawEntityProjectionMap);
596646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        appendAccountFromParameter(qb, uri);
596746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    }
596846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
596982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
597082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String[] projection, boolean distinct) {
597146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        setTablesAndProjectionMapForData(qb, uri, projection, distinct, null);
597246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
597346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
597446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
597546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * @param usageType when non-null {@link Tables#DATA_USAGE_STAT} is joined with the specified
597646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * type.
597746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
597846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
597946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            String[] projection, boolean distinct, Integer usageType) {
598082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5981ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.DATA);
598282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sb.append(" data");
598382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
5984a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, RawContacts.CONTACT_ID);
5985a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
5986a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
5987a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
59883296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
598946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (usageType != null) {
599046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            appendDataUsageStatJoin(sb, usageType, DataColumns.CONCRETE_ID);
599146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
599246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
599382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
5994f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov
5995f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        boolean useDistinct = distinct
5996f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov                || !mDbHelper.isInProjection(projection, DISTINCT_DATA_PROHIBITING_COLUMNS);
5997f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setDistinct(useDistinct);
5998f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setProjectionMap(useDistinct ? sDistinctDataProjectionMap : sDataProjectionMap);
599982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        appendAccountFromParameter(qb, uri);
6000ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    }
6001ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
60020a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb,
60030a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String[] projection) {
60040a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        StringBuilder sb = new StringBuilder();
6005ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.DATA);
60060a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        sb.append(" data");
6007a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
6008a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
60090a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
6010a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
6011a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sStatusUpdatesProjectionMap);
6012a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6013a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
60143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void setTablesAndProjectionMapForStreamItems(SQLiteQueryBuilder qb) {
60151dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro        qb.setTables(Tables.STREAM_ITEMS
60161dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + " JOIN " + Tables.RAW_CONTACTS + " ON ("
60171dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + StreamItemsColumns.CONCRETE_RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID
60181dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + ") JOIN " + Tables.CONTACTS + " ON ("
60191dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + RawContactsColumns.CONCRETE_CONTACT_ID + "=" + ContactsColumns.CONCRETE_ID + ")");
60203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        qb.setProjectionMap(sStreamItemsProjectionMap);
60213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
60223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
60233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void setTablesAndProjectionMapForStreamItemPhotos(SQLiteQueryBuilder qb) {
60241dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro        qb.setTables(Tables.PHOTO_FILES
60251dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + " JOIN " + Tables.STREAM_ITEM_PHOTOS + " ON ("
60261dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + StreamItemPhotosColumns.CONCRETE_PHOTO_FILE_ID + "="
60271dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + PhotoFilesColumns.CONCRETE_ID
60281dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + ") JOIN " + Tables.STREAM_ITEMS + " ON ("
60291dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "="
60301dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + StreamItemsColumns.CONCRETE_ID + ")");
60313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        qb.setProjectionMap(sStreamItemPhotosProjectionMap);
60323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
60333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
6034a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForEntities(SQLiteQueryBuilder qb, Uri uri,
6035a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection) {
6036a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
6037ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.ENTITIES);
6038a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(" data");
6039a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6040a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts.Entity.CONTACT_ID);
6041a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
6042a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, Contacts.Entity.DATA_ID);
6043a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, Contacts.Entity.DATA_ID);
6044a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6045a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
6046a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sEntityProjectionMap);
6047a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendAccountFromParameter(qb, uri);
6048a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6049a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6050a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactStatusUpdateJoin(StringBuilder sb, String[] projection,
6051a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String lastStatusUpdateIdColumn) {
6052a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection,
6053a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS,
6054a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
6055a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
6056a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
6057a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP)) {
6058a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
6059a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    + ContactsStatusUpdatesColumns.ALIAS +
6060a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + lastStatusUpdateIdColumn + "="
6061a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
60620a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
6063a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
60640a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
6065a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataStatusUpdateJoin(StringBuilder sb, String[] projection,
6066a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
6067b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
60680a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS,
60690a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_RES_PACKAGE,
60700a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_ICON,
60710a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_LABEL,
60720a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_TIMESTAMP)) {
60730a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
6074a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdatesColumns.CONCRETE_DATA_ID + "="
6075a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + dataIdColumn + ")");
60760a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
6077a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6078a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
607946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private void appendDataUsageStatJoin(StringBuilder sb, int usageType, String dataIdColumn) {
608046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        sb.append(" LEFT OUTER JOIN " + Tables.DATA_USAGE_STAT +
608146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "=" + dataIdColumn +
608246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                " AND " + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "=" + usageType + ")");
608346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
608446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
6085a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactPresenceJoin(StringBuilder sb, String[] projection,
6086a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn) {
6087a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection,
6088a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_PRESENCE, Contacts.CONTACT_CHAT_CAPABILITY)) {
6089a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
6090a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + contactIdColumn + " = "
6091a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + AggregatedPresenceColumns.CONCRETE_CONTACT_ID + ")");
6092a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
6093a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6094a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6095a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataPresenceJoin(StringBuilder sb, String[] projection,
6096a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
6097a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, Data.PRESENCE, Data.CHAT_CAPABILITY)) {
6098a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
6099a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdates.DATA_ID + "=" + dataIdColumn + ")");
6100a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
6101a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6102a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
610324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private boolean appendLocalDirectorySelectionIfNeeded(SQLiteQueryBuilder qb, long directoryId) {
6104385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directoryId == Directory.DEFAULT) {
6105385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " IN " + Tables.DEFAULT_DIRECTORY);
610624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            return true;
6107385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directoryId == Directory.LOCAL_INVISIBLE){
6108385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " NOT IN " + Tables.DEFAULT_DIRECTORY);
610924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            return true;
611024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
611124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        return false;
611224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
611324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
61144b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki    private String appendProfileRestriction(Uri uri, String profileColumn,
61154b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki            boolean suppressProfileCheck, String originalSelection) {
61164b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki        if (shouldIncludeProfile(uri, suppressProfileCheck)) {
61174b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki            return originalSelection;
61184b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki        } else {
61194b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki            final String SELECTION = "(" + profileColumn + " IS NULL OR " + profileColumn + "=0)";
61204b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki            if (TextUtils.isEmpty(originalSelection)) {
61214b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                return SELECTION;
61224b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki            } else {
61234b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                StringBuilder sb = new StringBuilder();
61244b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                sb.append("(");
61254b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                sb.append(originalSelection);
61264b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                sb.append(") AND ");
61274b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                sb.append(SELECTION);
61284b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                return sb.toString();
61294b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki            }
6130385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        }
6131385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov    }
6132385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov
61336ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro    private String prependProfileSortIfNeeded(Uri uri, String sortOrder,
61346ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro            boolean suppressProfileCheck) {
61356ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro        if (shouldIncludeProfile(uri, suppressProfileCheck)) {
613624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            if (TextUtils.isEmpty(sortOrder)) {
613724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                return Contacts.IS_USER_PROFILE + " DESC";
613824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            } else {
613924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                return Contacts.IS_USER_PROFILE + " DESC, " + sortOrder;
614024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
614124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
614224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        return sortOrder;
614324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
614424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
61456ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro    private boolean shouldIncludeProfile(Uri uri, boolean suppressProfileCheck) {
614624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // The user's profile may be returned alongside other contacts if it was requested and
614724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // the calling application has permission to read profile data.
6148377850d2dfd28eaf1b22273a50cfe066f6667ab9Dave Santoro        boolean profileRequested = readBooleanQueryParameter(uri, ContactsContract.ALLOW_PROFILE,
614924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                false);
61506ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro        if (profileRequested && !suppressProfileCheck) {
615124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermission(false);
615224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
615324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        return profileRequested;
615424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
615524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
61564a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) {
6157f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
6158f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
6159e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6160e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
6161e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
6162e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
6163fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
6164fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
6165e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
6166e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6167e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
6168e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
6169e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
6170e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
61714a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere(RawContacts.ACCOUNT_NAME + "="
61724a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
61734a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
61744a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountType));
61754a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        } else {
61764a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere("1");
61774a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
61784a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
61794a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
6180e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    private String appendAccountToSelection(Uri uri, String selection) {
6181f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
6182f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
6183e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6184e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
6185e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
6186e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
6187fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
6188fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
6189e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
6190e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6191e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
6192e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
6193e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
6194e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
6195e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "="
6196e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
6197e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + RawContacts.ACCOUNT_TYPE + "="
6198e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountType));
6199e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (!TextUtils.isEmpty(selection)) {
6200e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(" AND (");
6201e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(selection);
6202e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(')');
6203e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
6204e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selectionSb.toString();
6205e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } else {
6206e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selection;
6207e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
6208e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    }
6209e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
62107e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
6211c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
6212c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *
6213c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
6214c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
6215c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     */
6216f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private String getLimit(Uri uri) {
62172e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limitParam = getQueryParameter(uri, ContactsContract.LIMIT_PARAM_KEY);
6218c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limitParam == null) {
6219c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
6220c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
6221c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // make sure that the limit is a non-negative integer
6222c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        try {
6223c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int l = Integer.parseInt(limitParam);
6224c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (l < 0) {
6225c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
6226c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return null;
6227c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
6228c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return String.valueOf(l);
6229c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        } catch (NumberFormatException ex) {
6230c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
6231c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
6232c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
6233c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
6234c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
6235b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    @Override
6236f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
6237415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
6238f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (mode.equals("r")) {
6239f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            waitForAccess(mReadAccessLatch);
6240f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } else {
6241f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            waitForAccess(mWriteAccessLatch);
6242f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
6243415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
6244b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        int match = sUriMatcher.match(uri);
6245b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        switch (match) {
6246a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
6247afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                SQLiteDatabase db = mDbHelper.getReadableDatabase();
624824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
6249afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForRawContact(db, rawContactId, false);
6250afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                return openPhotoAssetFile(db, uri, mode,
625124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        Data._ID + "=" + Contacts.PHOTO_ID + " AND " +
625224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                                RawContacts.CONTACT_ID + "=?",
625324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        new String[]{String.valueOf(rawContactId)});
6254e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            }
6255b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
6256f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_ID_DISPLAY_PHOTO: {
6257f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
6258f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
6259f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Display photos retrieved by contact ID can only be read.");
6260f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6261afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                SQLiteDatabase db = mDbHelper.getReadableDatabase();
6262f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long contactId = Long.parseLong(uri.getPathSegments().get(1));
6263afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
6264afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                Cursor c = db.query(Tables.CONTACTS,
6265f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{Contacts.PHOTO_FILE_ID},
6266f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        Contacts._ID + "=?", new String[]{String.valueOf(contactId)},
6267f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        null, null, null);
6268f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
6269f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.moveToFirst();
6270f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    long photoFileId = c.getLong(0);
6271f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForRead(photoFileId);
6272f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
6273f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
6274f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6275f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6276f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6277f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_DISPLAY_PHOTO:
6278f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_ID_DISPLAY_PHOTO: {
6279f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
6280f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
6281f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Display photos retrieved by contact lookup key can only be read.");
6282f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6283f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                List<String> pathSegments = uri.getPathSegments();
6284f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                int segmentCount = pathSegments.size();
6285f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (segmentCount < 4) {
6286f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
6287f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Missing a lookup key", uri));
6288f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6289afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                SQLiteDatabase db = mDbHelper.getReadableDatabase();
6290f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                String lookupKey = pathSegments.get(2);
6291f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                String[] projection = new String[]{Contacts.PHOTO_FILE_ID};
6292f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (segmentCount == 5) {
6293f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    long contactId = Long.parseLong(pathSegments.get(3));
6294afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                    enforceProfilePermissionForContact(db, contactId, false);
6295f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
6296f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
6297afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
6298f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            projection, null, null, null, null, null,
6299f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
6300f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (c != null) {
6301f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        try {
6302f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            c.moveToFirst();
6303f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            long photoFileId = c.getLong(c.getColumnIndex(Contacts.PHOTO_FILE_ID));
6304f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            return openDisplayPhotoForRead(photoFileId);
6305f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        } finally {
6306f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            c.close();
6307f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
6308f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
6309f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6310f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6311f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
6312f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                setTablesAndProjectionMapForContacts(qb, uri, projection);
6313afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                long contactId = lookupContactIdByLookupKey(db, lookupKey);
6314afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
6315afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                Cursor c = qb.query(db, projection, Contacts._ID + "=?",
6316f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{String.valueOf(contactId)}, null, null, null);
6317f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
6318f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.moveToFirst();
6319f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    long photoFileId = c.getLong(c.getColumnIndex(Contacts.PHOTO_FILE_ID));
6320f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForRead(photoFileId);
6321f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
6322f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
6323f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6324f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6325f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6326f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case RAW_CONTACTS_ID_DISPLAY_PHOTO: {
6327f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
6328f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                boolean writeable = !mode.equals("r");
6329afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                SQLiteDatabase db = mDbHelper.getReadableDatabase();
6330afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForRawContact(db, rawContactId, writeable);
6331f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6332f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Find the primary photo data record for this raw contact.
6333f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
6334f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                String[] projection = new String[]{Data._ID, Photo.PHOTO_FILE_ID};
6335f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
6336afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                Cursor c = qb.query(db, projection,
6337f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        Data.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?",
6338f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{String.valueOf(rawContactId), Photo.CONTENT_ITEM_TYPE},
6339f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        null, null, Data.IS_PRIMARY + " DESC");
6340f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long dataId = 0;
6341f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long photoFileId = 0;
6342f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
6343f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (c.getCount() >= 1) {
6344f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        c.moveToFirst();
6345f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        dataId = c.getLong(0);
6346f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        photoFileId = c.getLong(1);
6347f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
6348f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
6349f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
6350f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6351f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6352f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // If writeable, open a writeable file descriptor that we can monitor.
6353f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // When the caller finishes writing content, we'll process the photo and
6354f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // update the data record.
6355f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (writeable) {
6356f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForWrite(rawContactId, dataId, uri, mode);
6357f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } else {
6358f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForRead(photoFileId);
6359f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6360f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6361f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6362f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case DISPLAY_PHOTO: {
6363f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long photoFileId = ContentUris.parseId(uri);
6364f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
6365f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
6366f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Display photos retrieved by key can only be read.");
6367f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6368f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                return openDisplayPhotoForRead(photoFileId);
6369f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6370f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6371e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            case DATA_ID: {
6372afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                SQLiteDatabase db = mDbHelper.getReadableDatabase();
637324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long dataId = Long.parseLong(uri.getPathSegments().get(1));
6374afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForData(db, dataId, false);
6375afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                return openPhotoAssetFile(db, uri, mode,
6376e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        Data._ID + "=? AND " + Data.MIMETYPE + "='" + Photo.CONTENT_ITEM_TYPE + "'",
637724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        new String[]{String.valueOf(dataId)});
6378d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
6379d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6380fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            case PROFILE_AS_VCARD: {
6381fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // When opening a contact as file, we pass back contents as a
6382fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // vCard-encoded stream. We build into a local buffer first,
6383fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // then pipe into MemoryFile once the exact size is known.
6384fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
6385fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(uri, localStream, null, null);
6386fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                return buildAssetFileDescriptor(localStream);
6387fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            }
638842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
6389fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            case CONTACTS_AS_VCARD: {
639042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // When opening a contact as file, we pass back contents as a
639142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // vCard-encoded stream. We build into a local buffer first,
639242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // then pipe into MemoryFile once the exact size is known.
639342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
6394fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(uri, localStream, null, null);
6395f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
639642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
639742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
639842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
639949d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                SQLiteDatabase db = mDbHelper.getReadableDatabase();
640042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKeys = uri.getPathSegments().get(2);
640142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String[] loopupKeyList = lookupKeys.split(":");
640242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final StringBuilder inBuilder = new StringBuilder();
6403fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                Uri queryUri = Contacts.CONTENT_URI;
640442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                int index = 0;
6405fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen
6406d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // SQLite has limits on how many parameters can be used
6407d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // so the IDs are concatenated to a query string here instead
640842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                for (String lookupKey : loopupKeyList) {
640942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    if (index == 0) {
6410d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append("(");
641142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    } else {
6412d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append(",");
641342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    }
641424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    long contactId = lookupContactIdByLookupKey(db, lookupKey);
6415afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                    enforceProfilePermissionForContact(db, contactId, false);
641624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    inBuilder.append(contactId);
6417fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                    if (mProfileIdCache.profileContactId == contactId) {
6418fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                        queryUri = queryUri.buildUpon().appendQueryParameter(
6419377850d2dfd28eaf1b22273a50cfe066f6667ab9Dave Santoro                                ContactsContract.ALLOW_PROFILE, "true").build();
6420fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                    }
642142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    index++;
642242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                }
642342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                inBuilder.append(')');
642442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + " IN " + inBuilder.toString();
6425d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6426d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // When opening a contact as file, we pass back contents as a
6427d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // vCard-encoded stream. We build into a local buffer first,
6428d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // then pipe into MemoryFile once the exact size is known.
6429d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
6430fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(queryUri, localStream, selection, null);
6431f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
6432d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
6433b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
6434b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            default:
6435fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new FileNotFoundException(mDbHelper.exceptionMessage("File does not exist",
6436fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        uri));
6437b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        }
6438b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
6439b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
6440afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro    private AssetFileDescriptor openPhotoAssetFile(SQLiteDatabase db, Uri uri, String mode,
6441afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro            String selection, String[] selectionArgs)
6442e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throws FileNotFoundException {
6443e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        if (!"r".equals(mode)) {
6444e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throw new FileNotFoundException(mDbHelper.exceptionMessage("Mode " + mode
6445e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                    + " not supported.", uri));
6446e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        }
6447e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
6448e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        String sql =
6449ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                "SELECT " + Photo.PHOTO + " FROM " + Views.DATA +
6450e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                " WHERE " + selection;
645108ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        try {
6452f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
6453f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    DatabaseUtils.blobFileDescriptorForQuery(db, sql, selectionArgs));
645408ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        } catch (SQLiteDoneException e) {
645508ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            // this will happen if the DB query returns no rows (i.e. contact does not exist)
645608ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            throw new FileNotFoundException(uri.toString());
645708ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        }
6458e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov    }
6459e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
6460f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
6461f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Opens a display photo from the photo store for reading.
6462f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param photoFileId The display photo file ID
6463f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @return An asset file descriptor that allows the file to be read.
6464f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @throws FileNotFoundException If no photo file for the given ID exists.
6465f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
6466f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private AssetFileDescriptor openDisplayPhotoForRead(long photoFileId)
6467f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            throws FileNotFoundException {
6468f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        PhotoStore.Entry entry = mPhotoStore.get(photoFileId);
6469f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (entry != null) {
6470f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            return makeAssetFileDescriptor(
6471f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    ParcelFileDescriptor.open(new File(entry.path),
6472f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            ParcelFileDescriptor.MODE_READ_ONLY),
6473f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    entry.size);
6474f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } else {
6475f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
6476f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            throw new FileNotFoundException("No photo file found for ID " + photoFileId);
6477f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
6478f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
6479f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6480f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
6481f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Opens a file descriptor for a photo to be written.  When the caller completes writing
6482f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * to the file (closing the output stream), the image will be parsed out and processed.
6483f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * If processing succeeds, the given raw contact ID's primary photo record will be
6484f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * populated with the inserted image (if no primary photo record exists, the data ID can
6485f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * be left as 0, and a new data record will be inserted).
6486f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param rawContactId Raw contact ID this photo entry should be associated with.
6487f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param dataId Data ID for a photo mimetype that will be updated with the inserted
6488f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     image.  May be set to 0, in which case the inserted image will trigger creation
6489f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     of a new primary photo image data row for the raw contact.
6490f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param uri The URI being used to access this file.
6491f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param mode Read/write mode string.
6492f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @return An asset file descriptor the caller can use to write an image file for the
6493f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     raw contact.
6494f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
6495f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private AssetFileDescriptor openDisplayPhotoForWrite(long rawContactId, long dataId, Uri uri,
6496f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            String mode) {
6497f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        try {
6498f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            return new AssetFileDescriptor(new MonitoredParcelFileDescriptor(rawContactId, dataId,
6499f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    ParcelFileDescriptor.open(File.createTempFile("img", null),
6500f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            ContentResolver.modeToMode(uri, mode))),
6501f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    0, AssetFileDescriptor.UNKNOWN_LENGTH);
6502f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } catch (IOException ioe) {
6503f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            Log.e(TAG, "Could not create temp image file in mode " + mode);
6504f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            return null;
6505f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
6506f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
6507f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6508f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
6509f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Parcel file descriptor wrapper that monitors when the file is closed.
6510f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * If the file contains a valid image, the image is either inserted into the given
6511f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * raw contact or updated in the given data row.
6512f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
6513f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private class MonitoredParcelFileDescriptor extends ParcelFileDescriptor {
6514f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        private final long mRawContactId;
6515f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        private final long mDataId;
6516f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        private MonitoredParcelFileDescriptor(long rawContactId, long dataId,
6517f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                ParcelFileDescriptor descriptor) {
6518f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            super(descriptor);
6519f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            mRawContactId = rawContactId;
6520f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            mDataId = dataId;
6521f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
6522f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6523f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        @Override
6524f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        public void close() throws IOException {
6525f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            try {
6526f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Check to see whether a valid image was written out.
6527f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                Bitmap b = BitmapFactory.decodeFileDescriptor(getFileDescriptor());
6528f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (b != null) {
6529f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    PhotoProcessor processor = new PhotoProcessor(b, mMaxDisplayPhotoDim,
6530f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            mMaxThumbnailPhotoDim);
6531f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6532f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    // Store the compressed photo in the photo store.
6533f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    long photoFileId = mPhotoStore.insert(processor);
6534f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6535f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    // Depending on whether we already had a data row to attach the photo to,
6536f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    // do an update or insert.
6537f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (mDataId != 0) {
6538f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Update the data record with the new photo.
6539f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        ContentValues updateValues = new ContentValues();
6540f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6541f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Signal that photo processing has already been handled.
6542f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        updateValues.put(DataRowHandlerForPhoto.SKIP_PROCESSING_KEY, true);
6543f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6544f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        if (photoFileId != 0) {
6545f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            updateValues.put(Photo.PHOTO_FILE_ID, photoFileId);
6546f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
6547f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        updateValues.put(Photo.PHOTO, processor.getThumbnailPhotoBytes());
6548f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        update(ContentUris.withAppendedId(Data.CONTENT_URI, mDataId), updateValues,
6549f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                null, null);
6550f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    } else {
6551f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Insert a new primary data record with the photo.
6552f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        ContentValues insertValues = new ContentValues();
6553f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6554f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Signal that photo processing has already been handled.
6555f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(DataRowHandlerForPhoto.SKIP_PROCESSING_KEY, true);
6556f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6557f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
6558f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Data.IS_PRIMARY, 1);
6559f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        if (photoFileId != 0) {
6560f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            insertValues.put(Photo.PHOTO_FILE_ID, photoFileId);
6561f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
6562f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Photo.PHOTO, processor.getThumbnailPhotoBytes());
6563f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insert(RawContacts.CONTENT_URI.buildUpon()
6564f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                .appendPath(String.valueOf(mRawContactId))
6565f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                .appendPath(RawContacts.Data.CONTENT_DIRECTORY).build(),
6566f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                insertValues);
6567f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
6568f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6569f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            } finally {
6570f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                super.close();
6571f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6572f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
6573f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
6574f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6575d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String CONTACT_MEMORY_FILE_NAME = "contactAssetFile";
6576d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6577d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
6578f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert     * Returns an {@link AssetFileDescriptor} backed by the
6579d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * contents of the given {@link ByteArrayOutputStream}.
6580d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
6581f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor buildAssetFileDescriptor(ByteArrayOutputStream stream) {
6582d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        try {
6583d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            stream.flush();
6584d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6585d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final byte[] byteData = stream.toByteArray();
6586d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6587f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
6588f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    ParcelFileDescriptor.fromData(byteData, CONTACT_MEMORY_FILE_NAME),
6589f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    byteData.length);
6590d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        } catch (IOException e) {
6591ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            Log.w(TAG, "Problem writing stream into an ParcelFileDescriptor: " + e.toString());
6592ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            return null;
6593d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
6594d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
6595d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6596f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd) {
6597f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return makeAssetFileDescriptor(fd, AssetFileDescriptor.UNKNOWN_LENGTH);
6598f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
6599f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
6600f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd, long length) {
6601f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return fd != null ? new AssetFileDescriptor(fd, 0, length) : null;
6602f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
6603f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
6604d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
6605d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Output {@link RawContacts} matching the requested selection in the vCard
6606d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * format to the given {@link OutputStream}. This method returns silently if
6607d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * any errors encountered.
6608d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
6609fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen    private void outputRawContactsAsVCard(Uri uri, OutputStream stream,
6610fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            String selection, String[] selectionArgs) {
6611d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final Context context = this.getContext();
6612dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen        int vcardconfig = VCardConfig.VCARD_TYPE_DEFAULT;
6613fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen        if(uri.getBooleanQueryParameter(
6614fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                Contacts.QUERY_PARAMETER_VCARD_NO_PHOTO, false)) {
6615dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen            vcardconfig |= VCardConfig.FLAG_REFRAIN_IMAGE_EXPORT;
6616dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen        }
66177a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        final VCardComposer composer =
6618dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen                new VCardComposer(context, vcardconfig, false);
6619108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        Writer writer = null;
6620108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        try {
6621108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            writer = new BufferedWriter(new OutputStreamWriter(stream));
6622fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            if (!composer.init(uri, selection, selectionArgs, null)) {
6623108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                Log.w(TAG, "Failed to init VCardComposer");
6624108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                return;
6625108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            }
6626d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6627108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            while (!composer.isAfterLast()) {
6628108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                writer.write(composer.createOneEntry());
6629108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            }
6630108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        } catch (IOException e) {
6631108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            Log.e(TAG, "IOException: " + e);
6632108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        } finally {
6633108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            composer.terminate();
6634108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            if (writer != null) {
6635108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                try {
6636108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                    writer.close();
6637108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                } catch (IOException e) {
6638108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                    Log.w(TAG, "IOException during closing output stream: " + e);
6639108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                }
6640d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
6641d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
6642d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
6643b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
66444f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
66454f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
6646415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
6647415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov        waitForAccess(mReadAccessLatch);
6648415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
6649a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
66504f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
6651b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS:
6652be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return Contacts.CONTENT_TYPE;
66532d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill            case CONTACTS_LOOKUP:
6654b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_ID:
6655b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
665624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE:
6657b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_ITEM_TYPE;
6658f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD:
665942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD:
666024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD:
6661f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                return Contacts.CONTENT_VCARD_TYPE;
6662f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            case CONTACTS_ID_PHOTO:
6663f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_ID_DISPLAY_PHOTO:
6664f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_DISPLAY_PHOTO:
6665f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_ID_DISPLAY_PHOTO:
6666f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case RAW_CONTACTS_ID_DISPLAY_PHOTO:
6667f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case DISPLAY_PHOTO:
6668f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                return "image/jpeg";
6669b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS:
667024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS:
6671be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return RawContacts.CONTENT_TYPE;
6672b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS_ID:
667324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID:
6674b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return RawContacts.CONTENT_ITEM_TYPE;
6675f481f22a9323fe338672f99b88b26c5f0725cd42David Brown            case DATA:
667624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA:
6677f481f22a9323fe338672f99b88b26c5f0725cd42David Brown                return Data.CONTENT_TYPE;
6678508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
6679b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getDataMimeType(ContentUris.parseId(uri));
668048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES:
668148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_TYPE;
668248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
668348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_ITEM_TYPE;
66849005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov            case PHONE_LOOKUP:
66859005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov                return PhoneLookup.CONTENT_TYPE;
668648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS:
668748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_TYPE;
668848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
668948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_ITEM_TYPE;
669048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS:
669148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_TYPE;
669248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID:
669348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_ITEM_TYPE;
6694b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
6695b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_TYPE;
6696b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
6697b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_ITEM_TYPE;
6698b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case SETTINGS:
6699b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Settings.CONTENT_TYPE;
6700b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
6701b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_TYPE;
6702c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS:
6703c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SUGGEST_MIME_TYPE;
6704c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT:
6705c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
6706d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES:
6707d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_TYPE;
6708d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID:
6709d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_ITEM_TYPE;
671061efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov            default:
671161efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov                return mLegacyApiSupport.getType(uri);
67124f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
67134f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
67147e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
671509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    public String[] getDefaultProjection(Uri uri) {
671609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        final int match = sUriMatcher.match(uri);
671709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        switch (match) {
671809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS:
671909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP:
672009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_ID:
672109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
672209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
672324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE:
672409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsProjectionMap.getColumnNames();
672509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
67268727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov            case CONTACTS_ID_ENTITIES:
672724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_ENTITIES:
67288727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov                return sEntityProjectionMap.getColumnNames();
67298727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov
673009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_VCARD:
673109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_MULTI_VCARD:
673224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD:
673309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsVCardProjectionMap.getColumnNames();
673409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
673509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS:
673609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS_ID:
673724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS:
673824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID:
673909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sRawContactsProjectionMap.getColumnNames();
674009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
674109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DATA_ID:
674209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES:
674309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES_ID:
674409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS:
674509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS_ID:
674609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS:
674709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS_ID:
674824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA:
674909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDataProjectionMap.getColumnNames();
675009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
675109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONE_LOOKUP:
675209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sPhoneLookupProjectionMap.getColumnNames();
675309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
675409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
675509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
675609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sAggregationExceptionsProjectionMap.getColumnNames();
675709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
675809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case SETTINGS:
675909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sSettingsProjectionMap.getColumnNames();
676009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
676109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES:
676209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES_ID:
676309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDirectoryProjectionMap.getColumnNames();
676409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
676509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            default:
676609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return null;
676709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
676809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    }
676909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
6770f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private class StructuredNameLookupBuilder extends NameLookupBuilder {
6771f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6772f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public StructuredNameLookupBuilder(NameSplitter splitter) {
6773f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            super(splitter);
6774f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6775f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6776f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
6777f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
6778f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name) {
677978fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov            mDbHelper.insertNameLookup(rawContactId, dataId, lookupType, name);
6780f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6781f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6782f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
6783f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected String[] getCommonNicknameClusters(String normalizedName) {
6784d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov            return mCommonNicknameCache.getCommonNicknameClusters(normalizedName);
6785f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6786f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
6787f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
67882d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov    public void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
6789d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
6790d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + RawContacts.CONTACT_ID +
6791d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.RAW_CONTACTS +
6792d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " JOIN " + Tables.NAME_LOOKUP +
6793d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " ON(" + RawContactsColumns.CONCRETE_ID + "="
6794d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
6795d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE normalized_name GLOB '");
6796e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
6797916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE +
6798916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " IN(" + CONTACT_LOOKUP_NAME_TYPES + "))");
6799e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    }
6800e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
68019a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    public boolean isPhoneNumber(String filter) {
68029a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        boolean atLeastOneDigit = false;
68039a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        int len = filter.length();
68049a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        for (int i = 0; i < len; i++) {
68059a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            char c = filter.charAt(i);
68069a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            if (c >= '0' && c <= '9') {
68079a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                atLeastOneDigit = true;
68089a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            } else if (c != '*' && c != '#' && c != '+' && c != 'N' && c != '.' && c != ';'
68099a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                    && c != '-' && c != '(' && c != ')' && c != ' ') {
68109a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                return false;
68119a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            }
68129a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
68139a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return atLeastOneDigit;
68149a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
68159a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
68164a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /**
68177a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * Takes components of a name from the query parameters and returns a cursor with those
68187a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * components as well as all missing components.  There is no database activity involved
68197a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * in this so the call can be made on the UI thread.
68207a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     */
68217a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private Cursor completeName(Uri uri, String[] projection) {
68227a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        if (projection == null) {
68237a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            projection = sDataProjectionMap.getColumnNames();
68247a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
68257a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
68267a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        ContentValues values = new ContentValues();
6827f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        DataRowHandlerForStructuredName handler = (DataRowHandlerForStructuredName)
6828f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                getDataRowHandler(StructuredName.CONTENT_ITEM_TYPE);
68297a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
68307a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        copyQueryParamsToContentValues(values, uri,
68317a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.DISPLAY_NAME,
68327a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PREFIX,
68337a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.GIVEN_NAME,
68347a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.MIDDLE_NAME,
68357a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.FAMILY_NAME,
68367a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.SUFFIX,
68377a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_NAME,
68387a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_FAMILY_NAME,
68397a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_MIDDLE_NAME,
68407a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_GIVEN_NAME
68417a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        );
68427a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
68437a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        handler.fixStructuredNameComponents(values, values);
68447a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
68457a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
68467a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        Object[] row = new Object[projection.length];
68477a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
68487a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            row[i] = values.get(projection[i]);
68497a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
68507a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        cursor.addRow(row);
68517a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        return cursor;
68527a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
68537a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
68547a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private void copyQueryParamsToContentValues(ContentValues values, Uri uri, String... columns) {
68557a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (String column : columns) {
68567a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            String param = uri.getQueryParameter(column);
68577a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            if (param != null) {
68587a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                values.put(column, param);
68597a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
68607a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
68617a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
68627a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
68637a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
68647a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    /**
68654a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     * Inserts an argument at the beginning of the selection arg list.
68664a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     */
68674a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private String[] insertSelectionArg(String[] selectionArgs, String arg) {
6868b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
6869b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
6870b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
6871b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
6872b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
68734a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            newSelectionArgs[0] = arg;
68744a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length);
6875b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
6876b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
6877b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
6878caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
68795e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private String[] appendProjectionArg(String[] projection, String arg) {
68805e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        if (projection == null) {
68815e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            return null;
68825e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        }
68835e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        final int length = projection.length;
68845e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        String[] newProjection = new String[length + 1];
68855e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        System.arraycopy(projection, 0, newProjection, 0, length);
68865e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        newProjection[length] = arg;
68875e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        return newProjection;
68885e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    }
68895e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
6890caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    protected Account getDefaultAccount() {
6891caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        AccountManager accountManager = AccountManager.get(getContext());
6892caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        try {
68935f1f4a062ac34d75d2dbf586702cbeb121cf09caDmitri Plotnikov            Account[] accounts = accountManager.getAccountsByType(DEFAULT_ACCOUNT_TYPE);
6894caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            if (accounts != null && accounts.length > 0) {
6895caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov                return accounts[0];
6896caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            }
6897caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        } catch (Throwable e) {
68986f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov            Log.e(TAG, "Cannot determine the default account for contacts compatibility", e);
6899caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        }
69006f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov        return null;
6901caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    }
6902f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
690373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    /**
690473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     * Returns true if the specified account type is writable.
690573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     */
690673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    protected boolean isWritableAccount(String accountType) {
6907bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        if (accountType == null) {
6908bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov            return true;
6909bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        }
6910bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov
691173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        Boolean writable = mAccountWritability.get(accountType);
691273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable != null) {
691373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            return writable;
691473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
691573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
6916627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        IContentService contentService = ContentResolver.getContentService();
6917627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
6918627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (SyncAdapterType sync : contentService.getSyncAdapterTypes()) {
6919627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (ContactsContract.AUTHORITY.equals(sync.authority) &&
692073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                        accountType.equals(sync.accountType)) {
692173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    writable = sync.supportsUploading();
692273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    break;
6923627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
6924627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
6925627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } catch (RemoteException e) {
6926627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            Log.e(TAG, "Could not acquire sync adapter types");
6927627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
692873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
692973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable == null) {
693073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            writable = false;
693173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
693273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
693373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.put(accountType, writable);
693473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        return writable;
6935627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
6936b4e61e064b59e8076df81b061add9fb358fd2ed9Dmitri Plotnikov
6937d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
6938f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static boolean readBooleanQueryParameter(Uri uri, String parameter,
6939f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean defaultValue) {
6940f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6941f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        // Manually parse the query, which is much faster than calling uri.getQueryParameter
6942f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
6943f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
6944f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
6945f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6946f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6947f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = query.indexOf(parameter);
6948f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (index == -1) {
6949f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
6950f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6951f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6952f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        index += parameter.length();
6953f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6954f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return !matchQueryParameter(query, index, "=0", false)
6955f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && !matchQueryParameter(query, index, "=false", true);
6956f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
6957f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6958f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private static boolean matchQueryParameter(String query, int index, String value,
6959f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean ignoreCase) {
6960f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int length = value.length();
6961f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return query.regionMatches(ignoreCase, index, value, 0, length)
6962f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && (query.length() == index + length || query.charAt(index + length) == '&');
6963f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
6964f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6965f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /**
6966f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * A fast re-implementation of {@link Uri#getQueryParameter}
6967f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     */
6968f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static String getQueryParameter(Uri uri, String parameter) {
6969f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
6970f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
6971f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return null;
6972f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6973f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6974f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int queryLength = query.length();
6975f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int parameterLength = parameter.length();
6976f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6977f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String value;
6978f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = 0;
6979f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        while (true) {
6980f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index = query.indexOf(parameter, index);
6981f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (index == -1) {
6982f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
69835fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            }
69845fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa
69855fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            // Should match against the whole parameter instead of its suffix.
69865fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            // e.g. The parameter "param" must not be found in "some_param=val".
69875fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            if (index > 0) {
69885fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                char prevChar = query.charAt(index - 1);
69895fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                if (prevChar != '?' && prevChar != '&') {
69905fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    // With "some_param=val1&param=val2", we should find second "param" occurrence.
69915fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    index += parameterLength;
69925fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    continue;
69935fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                }
6994f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
6995f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6996f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index += parameterLength;
6997f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6998f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (queryLength == index) {
6999f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
7000f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
7001f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7002f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (query.charAt(index) == '=') {
7003f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                index++;
7004f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                break;
7005f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
7006f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7007f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7008f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int ampIndex = query.indexOf('&', index);
7009f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (ampIndex == -1) {
7010f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index);
7011f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        } else {
7012f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index, ampIndex);
7013f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7014f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7015f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return Uri.decode(value);
7016f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
70175dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
70180dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    protected boolean isAggregationUpgradeNeeded() {
70190dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
70200dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            return false;
70210dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
70220dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
70230dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int version = Integer.parseInt(mDbHelper.getProperty(PROPERTY_AGGREGATION_ALGORITHM, "1"));
70240dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        return version < PROPERTY_AGGREGATION_ALGORITHM_VERSION;
70250dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
70260dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
7027bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void upgradeAggregationAlgorithmInBackground() {
70280dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // This upgrade will affect very few contacts, so it can be performed on the
70290dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // main thread during the initial boot after an OTA
70300dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
70310dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        Log.i(TAG, "Upgrading aggregation algorithm");
70320dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int count = 0;
70330dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        long start = SystemClock.currentThreadTimeMillis();
70340dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        try {
703549d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov            mDb = mDbHelper.getWritableDatabase();
70360dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.beginTransaction();
70370dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            Cursor cursor = mDb.query(true,
70380dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    Tables.RAW_CONTACTS + " r1 JOIN " + Tables.RAW_CONTACTS + " r2",
70390dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    new String[]{"r1." + RawContacts._ID},
70400dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    "r1." + RawContacts._ID + "!=r2." + RawContacts._ID +
70410dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.CONTACT_ID + "=r2." + RawContacts.CONTACT_ID +
70420dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.ACCOUNT_NAME + "=r2." + RawContacts.ACCOUNT_NAME +
70430dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.ACCOUNT_TYPE + "=r2." + RawContacts.ACCOUNT_TYPE,
70440dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    null, null, null, null, null);
70450dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            try {
70460dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                while (cursor.moveToNext()) {
70470dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    long rawContactId = cursor.getLong(0);
70480dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId,
70490dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                            RawContacts.AGGREGATION_MODE_DEFAULT, true);
70500dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    count++;
70510dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                }
70520dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            } finally {
70530dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                cursor.close();
70540dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            }
7055bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mContactAggregator.aggregateInTransaction(mTransactionContext, mDb);
7056bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            updateSearchIndexInTransaction();
70570dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.setTransactionSuccessful();
70580dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDbHelper.setProperty(PROPERTY_AGGREGATION_ALGORITHM,
70590dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    String.valueOf(PROPERTY_AGGREGATION_ALGORITHM_VERSION));
70600dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        } finally {
70610dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.endTransaction();
70620dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            long end = SystemClock.currentThreadTimeMillis();
70630dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            Log.i(TAG, "Aggregation algorithm upgraded for " + count
70640dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    + " contacts, in " + (end - start) + "ms");
70650dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
70660dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
70679a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
70689a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    /* Visible for testing */
70699a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    boolean isPhone() {
70709a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (!sIsPhoneInitialized) {
70719a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sIsPhone = new TelephonyManager(getContext()).isVoiceCapable();
70729a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sIsPhoneInitialized = true;
70739a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
70749a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return sIsPhone;
70759a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
707646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
707746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private boolean handleDataUsageFeedback(Uri uri) {
707846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final long currentTimeMillis = System.currentTimeMillis();
707946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String usageType = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
708046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] ids = uri.getLastPathSegment().trim().split(",");
708146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final ArrayList<Long> dataIds = new ArrayList<Long>();
708246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
708346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        for (String id : ids) {
708446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            dataIds.add(Long.valueOf(id));
708546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
708646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final boolean successful;
708746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (TextUtils.isEmpty(usageType)) {
708846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            Log.w(TAG, "Method for data usage feedback isn't specified. Ignoring.");
708946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            successful = false;
709046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } else {
709146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            successful = updateDataUsageStat(dataIds, usageType, currentTimeMillis) > 0;
709246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
709346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
709446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        // Handle old API. This doesn't affect the result of this entire method.
709546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] questionMarks = new String[ids.length];
709646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        Arrays.fill(questionMarks, "?");
709746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String where = Data._ID + " IN (" + TextUtils.join(",", questionMarks) + ")";
709846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final Cursor cursor = mDb.query(
7099ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                Views.DATA,
710046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                new String[] { Data.CONTACT_ID },
710146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                where, ids, null, null, null);
710246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        try {
710346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            while (cursor.moveToNext()) {
710446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mSelectionArgs1[0] = cursor.getString(0);
710546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                ContentValues values2 = new ContentValues();
710646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                values2.put(Contacts.LAST_TIME_CONTACTED, currentTimeMillis);
710746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDb.update(Tables.CONTACTS, values2, Contacts._ID + "=?", mSelectionArgs1);
710846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDb.execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
710946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDb.execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
711046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
711146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } finally {
711246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            cursor.close();
711346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
711446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
711546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        return successful;
711646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
711746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
711846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
711946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Update {@link Tables#DATA_USAGE_STAT}.
712046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     *
712146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * @return the number of rows affected.
712246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
7123f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa    @VisibleForTesting
7124f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa    /* package */ int updateDataUsageStat(
7125f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa            List<Long> dataIds, String type, long currentTimeMillis) {
712646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final int typeInt = sDataUsageTypeMap.get(type);
712746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String where = DataUsageStatColumns.DATA_ID + " =? AND "
712846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                + DataUsageStatColumns.USAGE_TYPE_INT + " =?";
712946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] columns =
713046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                new String[] { DataUsageStatColumns._ID, DataUsageStatColumns.TIMES_USED };
713146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final ContentValues values = new ContentValues();
713246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        for (Long dataId : dataIds) {
713346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            final String[] args = new String[] { dataId.toString(), String.valueOf(typeInt) };
713446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            mDb.beginTransaction();
713546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            try {
713646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                final Cursor cursor = mDb.query(Tables.DATA_USAGE_STAT, columns, where, args,
713746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        null, null, null);
713846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                try {
713946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (cursor.getCount() > 0) {
714046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        if (!cursor.moveToFirst()) {
714146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            Log.e(TAG,
714246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    "moveToFirst() failed while getAccount() returned non-zero.");
714346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        } else {
714446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.clear();
714546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.put(DataUsageStatColumns.TIMES_USED, cursor.getInt(1) + 1);
714646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.put(DataUsageStatColumns.LAST_TIME_USED, currentTimeMillis);
714746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            mDb.update(Tables.DATA_USAGE_STAT, values,
714846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    DataUsageStatColumns._ID + " =?",
714946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    new String[] { cursor.getString(0) });
715046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        }
715146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    } else {
715246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.clear();
715346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.DATA_ID, dataId);
715446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.USAGE_TYPE_INT, typeInt);
715546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.TIMES_USED, 1);
715646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.LAST_TIME_USED, currentTimeMillis);
715746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        mDb.insert(Tables.DATA_USAGE_STAT, null, values);
715846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    }
715946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    mDb.setTransactionSuccessful();
716046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                } finally {
716146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    cursor.close();
716246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
716346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            } finally {
716446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDb.endTransaction();
716546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
716646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
716746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
716846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        return dataIds.size();
716946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
717046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
717146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
717246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Returns a sort order String for promoting data rows (email addresses, phone numbers, etc.)
717346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * associated with a primary account. The primary account should be supplied from applications
717446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * with {@link ContactsContract#PRIMARY_ACCOUNT_NAME} and
717546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * {@link ContactsContract#PRIMARY_ACCOUNT_TYPE}. Null will be returned when the primary
717646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * account isn't available.
717746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
717846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private String getAccountPromotionSortOrder(Uri uri) {
717946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String primaryAccountName =
718046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME);
718146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String primaryAccountType =
718246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE);
718346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
718446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        // Data rows associated with primary account should be promoted.
718546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (!TextUtils.isEmpty(primaryAccountName)) {
718646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            StringBuilder sb = new StringBuilder();
718746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            sb.append("(CASE WHEN " + RawContacts.ACCOUNT_NAME + "=");
718846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            DatabaseUtils.appendEscapedSQLString(sb, primaryAccountName);
718946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            if (!TextUtils.isEmpty(primaryAccountType)) {
719046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                sb.append(" AND " + RawContacts.ACCOUNT_TYPE + "=");
719146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DatabaseUtils.appendEscapedSQLString(sb, primaryAccountType);
719246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
719346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            sb.append(" THEN 0 ELSE 1 END)");
719446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            return sb.toString();
719546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } else {
719646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            return null;
719746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
719846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
71994f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
7200