ContactsProvider2.java revision af43bfb95070c234ae7090f6041f6fc62366313a
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;
740bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmannimport android.content.pm.PackageManager;
750bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmannimport android.content.pm.PackageManager.NameNotFoundException;
76f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringertimport android.content.res.AssetFileDescriptor;
773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmannimport android.content.res.Resources;
780bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmannimport android.content.res.Resources.NotFoundException;
79e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikovimport android.database.CrossProcessCursor;
804f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.Cursor;
81e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikovimport android.database.CursorWindow;
82ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.database.CursorWrapper;
83ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.database.DatabaseUtils;
8409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor;
8509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor.RowBuilder;
864f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteDatabase;
8708ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwoodimport android.database.sqlite.SQLiteDoneException;
884f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteQueryBuilder;
89f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.graphics.Bitmap;
90f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.graphics.BitmapFactory;
914f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.net.Uri;
92d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.net.Uri.Builder;
93c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoroimport android.os.AsyncTask;
94bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Binder;
956ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.os.Bundle;
96bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Handler;
97bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.HandlerThread;
98bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Message;
99ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringertimport android.os.ParcelFileDescriptor;
100c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoroimport android.os.ParcelFileDescriptor.AutoCloseInputStream;
101bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Process;
102b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.os.RemoteException;
10315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikovimport android.os.StrictMode;
1040dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikovimport android.os.SystemClock;
1050e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport android.os.SystemProperties;
1063d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.preference.PreferenceManager;
107508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkeyimport android.provider.BaseColumns;
1083de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract;
109b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions;
11097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Email;
11197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
11297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Im;
11397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Nickname;
1146d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Note;
11597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Organization;
11697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Phone;
11797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Photo;
1184928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.SipAddress;
11997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
12097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
121ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.provider.ContactsContract.ContactCounts;
1223de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Contacts;
1235b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikovimport android.provider.ContactsContract.Contacts.AggregationSuggestions;
1243de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Data;
12571340347b4862d4b1368a5d69d1667e2245952e4Daisuke Miyakawaimport android.provider.ContactsContract.DataUsageFeedback;
126d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.provider.ContactsContract.Directory;
127f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.provider.ContactsContract.DisplayPhoto;
1283de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Groups;
129bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.provider.ContactsContract.Intents;
1303de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.PhoneLookup;
1311dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoroimport android.provider.ContactsContract.PhotoFiles;
13209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.provider.ContactsContract.ProviderStatus;
1333de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.RawContacts;
134916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikovimport android.provider.ContactsContract.SearchSnippetColumns;
1353de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Settings;
13682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikovimport android.provider.ContactsContract.StatusUpdates;
1373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmannimport android.provider.ContactsContract.StreamItemPhotos;
138f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.provider.ContactsContract.StreamItems;
13997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.LiveFolders;
14097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.OpenableColumns;
14197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.SyncStateContract;
142a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.telephony.PhoneNumberUtils;
1439a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikovimport android.telephony.TelephonyManager;
144d5ef5903570e533a501abe6a8e3d533fdb5318fcFlavio Lerdaimport android.text.Html;
145d5ef5903570e533a501abe6a8e3d533fdb5318fcFlavio Lerdaimport android.text.SpannableString;
146a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.text.TextUtils;
147c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.util.Log;
1484f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
149108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawaimport java.io.BufferedWriter;
150d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.ByteArrayOutputStream;
151f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport java.io.File;
152b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport java.io.FileNotFoundException;
153d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.IOException;
154d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.OutputStream;
155108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawaimport java.io.OutputStreamWriter;
156108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawaimport java.io.Writer;
15742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.text.SimpleDateFormat;
1587e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport java.util.ArrayList;
15946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawaimport java.util.Arrays;
1605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.Collections;
16142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.util.Date;
162b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport java.util.HashMap;
1630e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.HashSet;
1645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.List;
165622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkeyimport java.util.Locale;
166b5a4add17815167d20a90645779df34cdf45280dFred Quintanaimport java.util.Map;
1670e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.Set;
168ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikovimport java.util.concurrent.CountDownLatch;
1694f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1704f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/**
1714f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Contacts content provider. The contract between this provider and applications
1724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * is defined in {@link ContactsContract}.
1734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
1745b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanapublic class ContactsProvider2 extends SQLiteContentProvider implements OnAccountsUpdateListener {
175caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
176bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final String TAG = "ContactsProvider";
177bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov
178bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE);
1794f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
18015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_INITIALIZE = 0;
18115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_OPEN_WRITE_ACCESS = 1;
18215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS = 2;
18315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_ACCOUNTS = 3;
18415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_LOCALE = 4;
18515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM = 5;
18605e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_SEARCH_INDEX = 6;
18705e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_PROVIDER_STATUS = 7;
18805e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_DIRECTORIES = 8;
18905e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_CHANGE_LOCALE = 9;
190f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int BACKGROUND_TASK_CLEANUP_PHOTOS = 10;
191619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Default for the maximum number of returned aggregation suggestions. */
1933cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DEFAULT_MAX_SUGGESTIONS = 5;
1943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /** Limit for the maximum number of social stream items to store under a raw contact. */
1963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int MAX_STREAM_ITEMS_PER_RAW_CONTACT = 5;
1973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
198f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /** Rate limit (in ms) for photo cleanup.  Do it at most once per day. */
199f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int PHOTO_CLEANUP_RATE_LIMIT = 24 * 60 * 60 * 1000;
200f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
2013d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /**
202b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov     * Property key for the legacy contact import version. The need for a version
2033d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * as opposed to a boolean flag is that if we discover bugs in the contact import process,
2043d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * we can trigger re-import by incrementing the import version.
2053d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     */
206b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final String PROPERTY_CONTACTS_IMPORTED = "contacts_imported_v1";
207b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final int PROPERTY_CONTACTS_IMPORT_VERSION = 1;
20851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    private static final String PREF_LOCALE = "locale";
2093d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2100dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static final String PROPERTY_AGGREGATION_ALGORITHM = "aggregation_v2";
2110dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static final int PROPERTY_AGGREGATION_ALGORITHM_VERSION = 2;
2120dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
2130e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff    private static final String AGGREGATE_CONTACTS = "sync.contacts.aggregate";
2140e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff
215a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
2164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2172f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    /**
2182f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     * Used to insert a column into strequent results, which enables SQL to sort the list using
2192f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     * the total times contacted. See also {@link #sStrequentFrequentProjectionMap}.
2202f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     */
2212f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    private static final String TIMES_USED_SORT_COLUMN = "times_used_sort";
2225e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
223d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final String STREQUENT_ORDER_BY = Contacts.STARRED + " DESC, "
2242f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            + TIMES_USED_SORT_COLUMN + " DESC, "
2259b43551f1ce33b79141772737a262ce609bd0cebMegha Joshi            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
226d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final String STREQUENT_LIMIT =
227d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            "(SELECT COUNT(1) FROM " + Tables.CONTACTS + " WHERE "
228d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            + Contacts.STARRED + "=1) + 25";
229d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
23045ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa    private static final String FREQUENT_ORDER_BY = DataUsageStatColumns.TIMES_USED + " DESC,"
23145ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
23245ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa
2336e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_CONTACTS_TABLE =
2349b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.CONTACTS + " SET " + Contacts.TIMES_CONTACTED + "=" +
2359b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + Contacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
2369b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + Contacts.TIMES_CONTACTED + " + 1) END WHERE " + Contacts._ID + "=?";
2379b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
2386e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE =
2399b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.RAW_CONTACTS + " SET " + RawContacts.TIMES_CONTACTED + "=" +
2409b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + RawContacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
2419b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + RawContacts.TIMES_CONTACTED + " + 1) END WHERE " + RawContacts.CONTACT_ID + "=?";
2429b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
243de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa    /* package */ static final String PHONEBOOK_COLLATOR_NAME = "PHONEBOOK";
244de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa
2453716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    // Regex for splitting query strings - we split on any group of non-alphanumeric characters,
2463716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    // excluding the @ symbol.
2473716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    /* package */ static final String QUERY_TOKENIZER_REGEX = "[^\\w@]+";
2483716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
249d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS = 1000;
250d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS_ID = 1001;
2515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP = 1002;
2525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID = 1003;
253a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_DATA = 1004;
2545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_FILTER = 1005;
2555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT = 1006;
2565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT_FILTER = 1007;
2575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_GROUP = 1008;
258a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_PHOTO = 1009;
259f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_ID_DISPLAY_PHOTO = 1010;
260f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_LOOKUP_DISPLAY_PHOTO = 1011;
261f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_LOOKUP_ID_DISPLAY_PHOTO = 1012;
262f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_AS_VCARD = 1013;
263f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_AS_MULTI_VCARD = 1014;
264f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_LOOKUP_DATA = 1015;
265f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_LOOKUP_ID_DATA = 1016;
266f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_ID_ENTITIES = 1017;
267f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_LOOKUP_ENTITIES = 1018;
268f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_LOOKUP_ID_ENTITIES = 1019;
269f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_ID_STREAM_ITEMS = 1020;
270f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_LOOKUP_STREAM_ITEMS = 1021;
271f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int CONTACTS_LOOKUP_ID_STREAM_ITEMS = 1022;
27245ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa    private static final int CONTACTS_FREQUENT = 1023;
2734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2745ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS = 2002;
2755ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_ID = 2003;
2765ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_DATA = 2004;
27746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITY_ID = 2005;
278f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int RAW_CONTACTS_ID_DISPLAY_PHOTO = 2006;
279f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int RAW_CONTACTS_ID_STREAM_ITEMS = 2007;
2804f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2816bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
2826bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
283ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
28448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_ID = 3003;
28548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_FILTER = 3004;
28648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS = 3005;
28748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_ID = 3006;
28848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_LOOKUP = 3007;
28948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_FILTER = 3008;
29048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS = 3009;
29148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS_ID = 3010;
292a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2936bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
2946bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
295b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
296b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
297b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
29882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES = 7000;
29982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES_ID = 7001;
3001f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
30131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
30231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
303eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final int SETTINGS = 9000;
304eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
305ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
306ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
307ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
308ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
30935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
310b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private static final int SYNCSTATE_ID = 11001;
31135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
312c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SUGGESTIONS = 12001;
313c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SHORTCUT = 12002;
314c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
3151b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS = 14000;
3161b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_WITH_PHONES = 14001;
3171b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_FAVORITES = 14002;
3181b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_GROUP_NAME = 14003;
3191b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
32046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITIES = 15001;
32146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
32209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private static final int PROVIDER_STATUS = 16001;
32309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
324d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES = 17001;
325d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES_ID = 17002;
326d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3277a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private static final int COMPLETE_NAME = 18000;
3287a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
32924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE = 19000;
33024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_ENTITIES = 19001;
33124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_DATA = 19002;
33224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_DATA_ID = 19003;
33324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_AS_VCARD = 19004;
33424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS = 19005;
33524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID = 19006;
33624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID_DATA = 19007;
33724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID_ENTITIES = 19008;
33824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
33946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final int DATA_USAGE_FEEDBACK_ID = 20001;
34046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
3413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS = 21000;
3423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_PHOTOS = 21001;
3433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID = 21002;
3443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID_PHOTOS = 21003;
3453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID_PHOTOS_ID = 21004;
3463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_LIMIT = 21005;
3473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
348f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int DISPLAY_PHOTO = 22000;
349f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int PHOTO_DIMENSIONS = 22001;
350f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
351dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID =
352dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
353dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME
354dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
355dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE
35643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AND ("
35743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + GroupsColumns.CONCRETE_DATA_SET
35843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + "=" + RawContactsColumns.CONCRETE_DATA_SET + " OR "
35943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + GroupsColumns.CONCRETE_DATA_SET + " IS NULL AND "
36043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + RawContactsColumns.CONCRETE_DATA_SET + " IS NULL)"
361dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " AND " + Groups.FAVORITES + " != 0";
362dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
363dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID =
364dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
365dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
366dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
367dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
36843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AND ("
36943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + GroupsColumns.CONCRETE_DATA_SET + "="
37043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + RawContactsColumns.CONCRETE_DATA_SET + " OR "
37143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + GroupsColumns.CONCRETE_DATA_SET + " IS NULL AND "
37243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + RawContactsColumns.CONCRETE_DATA_SET + " IS NULL)"
37343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + " AND " + Groups.AUTO_ADD + " != 0";
374dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
375dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String[] PROJECTION_GROUP_ID
376dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            = new String[]{Tables.GROUPS + "." + Groups._ID};
377dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
378dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_GROUPMEMBERSHIP_DATA = DataColumns.MIMETYPE_ID + "=? "
379dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.GROUP_ROW_ID + "=? "
380dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.RAW_CONTACT_ID + "=?";
381dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
382dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_STARRED_FROM_RAW_CONTACTS =
383dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            "SELECT " + RawContacts.STARRED
384dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " FROM " + Tables.RAW_CONTACTS + " WHERE " + RawContacts._ID + "=?";
385dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
386e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    public class AddressBookCursor extends CursorWrapper implements CrossProcessCursor {
387e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        private final CrossProcessCursor mCursor;
388e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        private final Bundle mBundle;
389e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
390e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public AddressBookCursor(CrossProcessCursor cursor, String[] titles, int[] counts) {
391e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            super(cursor);
392e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mCursor = cursor;
393e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mBundle = new Bundle();
394e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mBundle.putStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES, titles);
395e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mBundle.putIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS, counts);
396e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
397e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
398e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        @Override
399e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public Bundle getExtras() {
400e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return mBundle;
401e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
402e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
403e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        @Override
404e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public void fillWindow(int pos, CursorWindow window) {
405e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mCursor.fillWindow(pos, window);
406e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
407e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
408e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        @Override
409e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public CursorWindow getWindow() {
410e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return mCursor.getWindow();
411e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
412e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
413e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        @Override
414e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public boolean onMove(int oldPosition, int newPosition) {
415e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return mCursor.onMove(oldPosition, newPosition);
416e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
417e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    }
418e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
419d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private interface DataContactsQuery {
420f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        public static final String TABLE = "data "
421f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) "
422f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN contacts ON (raw_contacts.contact_id = contacts._id)";
42367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
42467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
4256cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
4266802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            RawContactsColumns.CONCRETE_ACCOUNT_TYPE,
4276802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            RawContactsColumns.CONCRETE_ACCOUNT_NAME,
42843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            RawContactsColumns.CONCRETE_DATA_SET,
4293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
430f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            ContactsColumns.CONCRETE_ID
431ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
432ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
433d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
4346802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        public static final int ACCOUNT_TYPE = 1;
4356802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        public static final int ACCOUNT_NAME = 2;
43643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        public static final int DATA_SET = 3;
43743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        public static final int DATA_ID = 4;
43843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        public static final int CONTACT_ID = 5;
439ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
4401f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
441f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov    interface RawContactsQuery {
44219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String TABLE = Tables.RAW_CONTACTS;
44319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
44419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String[] COLUMNS = new String[] {
445ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.DELETED,
446ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
447ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
44843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.DATA_SET,
44919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        };
45019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
45119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int DELETED = 0;
452ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_TYPE = 1;
453ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_NAME = 2;
45443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int DATA_SET = 3;
45519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    }
45619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
457c76cdd0723b99f478c9ba5329d14a971cd8dfb3dCostin Manolache    public static final String DEFAULT_ACCOUNT_TYPE = "com.google";
458caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
45971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    /** Sql where statement for filtering on groups. */
46071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private static final String CONTACTS_IN_GROUP_SELECT =
46171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov            Contacts._ID + " IN "
46271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + "(SELECT " + RawContacts.CONTACT_ID
46371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " FROM " + Tables.RAW_CONTACTS
46471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + " IN "
46571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
46671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " FROM " + Tables.DATA_JOIN_MIMETYPES
46771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " WHERE " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
46871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "' AND " + GroupMembership.GROUP_ROW_ID + "="
46971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "(SELECT " + Tables.GROUPS + "." + Groups._ID
47071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " FROM " + Tables.GROUPS
47171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " WHERE " + Groups.TITLE + "=?)))";
47271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov
473a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating DIRTY flag on multiple raw contacts */
474a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_DIRTY_SQL =
475a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
476a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.DIRTY + "=1" +
477a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
478a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
479a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating VERSION on multiple raw contacts */
480a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_VERSION_SQL =
481a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
482a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.VERSION + " = " + RawContacts.VERSION + " + 1" +
483a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
484a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
485c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Current contacts - those contacted within the last 3 days (in seconds)
486c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_CURRENT = 3 * 24 * 60 * 60;
487c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
488c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Recent contacts - those contacted within the last 30 days (in seconds)
489c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_RECENT = 30 * 24 * 60 * 60;
490c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
491f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa    private static final String TIME_SINCE_LAST_USED =
492f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa            "(strftime('%s', 'now') - " + DataUsageStatColumns.LAST_TIME_USED + "/1000)";
493f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa
494c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    /*
495c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * Sorting order for email address suggestions: first starred, then the rest.
4962262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * second in_visible_group, then the rest.
4972262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * Within the four (starred/unstarred, in_visible_group/not-in_visible_group) groups
4982262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * - three buckets: very recently contacted, then fairly
499c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * recently contacted, then the rest.  Within each of the bucket - descending count
50046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * of times contacted (both for data row and for contact row). If all else fails, alphabetical.
50146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * (Super)primary email address is returned before other addresses for the same contact.
502c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     */
503c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final String EMAIL_FILTER_SORT_ORDER =
5042262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa        Contacts.STARRED + " DESC, "
5052262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa        + Contacts.IN_VISIBLE_GROUP + " DESC, "
506f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa        + "(CASE WHEN " + TIME_SINCE_LAST_USED + " < " + EMAIL_FILTER_CURRENT
50746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " THEN 0 "
508f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa                + " WHEN " + TIME_SINCE_LAST_USED + " < " + EMAIL_FILTER_RECENT
50946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " THEN 1 "
51046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " ELSE 2 END), "
51146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + DataUsageStatColumns.TIMES_USED + " DESC, "
51246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + Contacts.DISPLAY_NAME + ", "
51346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + Data.CONTACT_ID + ", "
514c591cc2ffecdd0038f787a133606752752294c13Daisuke Miyakawa        + Data.IS_SUPER_PRIMARY + " DESC, "
515c591cc2ffecdd0038f787a133606752752294c13Daisuke Miyakawa        + Data.IS_PRIMARY + " DESC";
51646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
51746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /** Currently same as {@link #EMAIL_FILTER_SORT_ORDER} */
51846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final String PHONE_FILTER_SORT_ORDER = EMAIL_FILTER_SORT_ORDER;
519c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
520916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Name lookup types used for contact filtering */
521916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private static final String CONTACT_LOOKUP_NAME_TYPES =
522916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NAME_COLLATION_KEY + "," +
523916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.EMAIL_BASED_NICKNAME + "," +
52492ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov            NameLookupType.NICKNAME;
525916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
526f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    /**
527f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * If any of these columns are used in a Data projection, there is no point in
528f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * using the DISTINCT keyword, which can negatively affect performance.
529f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     */
530f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    private static final String[] DISTINCT_DATA_PROHIBITING_COLUMNS = {
531f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data._ID,
532f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.RAW_CONTACT_ID,
533f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.NAME_RAW_CONTACT_ID,
534f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_NAME,
535f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_TYPE,
53643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            RawContacts.DATA_SET,
53743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
538f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.DIRTY,
539f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.NAME_VERIFIED,
540f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.SOURCE_ID,
541f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.VERSION,
542f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    };
543916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
544f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsColumns = ProjectionMap.builder()
545f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CUSTOM_RINGTONE)
546f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME)
547f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_ALTERNATIVE)
548f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_SOURCE)
549f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.IN_VISIBLE_GROUP)
550f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LAST_TIME_CONTACTED)
551f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LOOKUP_KEY)
552f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME)
553f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME_STYLE)
554f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHOTO_ID)
555f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            .add(Contacts.PHOTO_FILE_ID)
5563d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_URI)
5573d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_THUMBNAIL_URI)
558f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SEND_TO_VOICEMAIL)
559f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_ALTERNATIVE)
560f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_PRIMARY)
561f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.STARRED)
562f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.TIMES_CONTACTED)
563cf832869bcf91b8037d8b7f510a3a213b30764a3Dmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
564f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
565f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
566f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsPresenceColumns = ProjectionMap.builder()
567f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
568f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE)
569f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
570f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
571f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
572f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
573f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
574f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
575f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
576f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
577f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
578f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
579f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
580f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
581f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
582f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
583f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSnippetColumns = ProjectionMap.builder()
58403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET)
585f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
586f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
587f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactColumns = ProjectionMap.builder()
588f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_NAME)
589f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_TYPE)
59043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(RawContacts.DATA_SET)
59143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(RawContacts.ACCOUNT_TYPE_AND_DATA_SET)
592f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DIRTY)
593f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.NAME_VERIFIED)
594f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SOURCE_ID)
595f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.VERSION)
596f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
597f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
598f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactSyncColumns = ProjectionMap.builder()
599f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC1)
600f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC2)
601f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC3)
602f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC4)
603f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
604f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
605f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataColumns = ProjectionMap.builder()
606f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA1)
607f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA2)
608f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA3)
609f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA4)
610f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA5)
611f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA6)
612f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA7)
613f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA8)
614f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA9)
615f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA10)
616f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA11)
617f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA12)
618f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA13)
619f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA14)
620f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA15)
621f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA_VERSION)
622f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_PRIMARY)
623f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_SUPER_PRIMARY)
624f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.MIMETYPE)
625f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RES_PACKAGE)
626f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC1)
627f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC2)
628f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC3)
629f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC4)
630f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(GroupMembership.GROUP_SOURCE_ID)
631f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
632f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
633f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactPresenceColumns = ProjectionMap.builder()
634f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
635f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.PRESENCE)
636f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
637f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.CHAT_CAPABILITY)
638f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
639f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
640f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
641f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
642f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
643f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
644f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
645f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
646f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
647f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
648f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
649f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
650f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataPresenceColumns = ProjectionMap.builder()
651f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.PRESENCE, Tables.PRESENCE + "." + StatusUpdates.PRESENCE)
652f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CHAT_CAPABILITY, Tables.PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
653f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS, StatusUpdatesColumns.CONCRETE_STATUS)
654f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_TIMESTAMP, StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
655f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_RES_PACKAGE, StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
656f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_LABEL, StatusUpdatesColumns.CONCRETE_STATUS_LABEL)
657f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_ICON, StatusUpdatesColumns.CONCRETE_STATUS_ICON)
658f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
659f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
660038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    /** Contains just BaseColumns._COUNT */
661f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sCountProjectionMap = ProjectionMap.builder()
662f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(BaseColumns._COUNT, "COUNT(*)")
663f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
664f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
665e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    /** Contains just the contacts columns */
666f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionMap = ProjectionMap.builder()
667f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts._ID)
668f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
669f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.NAME_RAW_CONTACT_ID)
67024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(Contacts.IS_USER_PROFILE)
671f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
672f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsPresenceColumns)
673f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
674f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
675916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Contains just the contacts columns */
676f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionWithSnippetMap = ProjectionMap.builder()
677f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
678f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sSnippetColumns)
679f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
680916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
6815e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    /** Used for pushing starred contacts to the top of a times contacted list **/
682f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentStarredProjectionMap = ProjectionMap.builder()
683f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
6842f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, String.valueOf(Long.MAX_VALUE))
685f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
686f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
687f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentFrequentProjectionMap = ProjectionMap.builder()
688f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
6892f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, "SUM(" + DataUsageStatColumns.CONCRETE_TIMES_USED + ")")
690f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
691f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6924928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    /**
6934928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * Used for Strequent Uri with {@link ContactsContract#STREQUENT_PHONE_ONLY}, which allows
6944928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * users to obtain part of Data columns. Right now Starred part just returns NULL for
6954928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * those data columns (frequent part should return real ones in data table).
6964928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     **/
6974928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    private static final ProjectionMap sStrequentPhoneOnlyStarredProjectionMap
6984928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            = ProjectionMap.builder()
6994928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .addAll(sContactsProjectionMap)
7004928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, String.valueOf(Long.MAX_VALUE))
7014928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.NUMBER, "NULL")
7024928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.TYPE, "NULL")
7034928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.LABEL, "NULL")
7044928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .build();
7054928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
7064928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    /**
7074928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * Used for Strequent Uri with {@link ContactsContract#STREQUENT_PHONE_ONLY}, which allows
7084928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * users to obtain part of Data columns. We hard-code {@link Contacts#IS_USER_PROFILE} to NULL,
7094928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * because sContactsProjectionMap specifies a field that doesn't exist in the view behind the
7104928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * query that uses this projection map.
7114928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     **/
7124928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    private static final ProjectionMap sStrequentPhoneOnlyFrequentProjectionMap
7134928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            = ProjectionMap.builder()
7144928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .addAll(sContactsProjectionMap)
7154928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, DataUsageStatColumns.CONCRETE_TIMES_USED)
7164928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.NUMBER)
7174928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.TYPE)
7184928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.LABEL)
7194928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Contacts.IS_USER_PROFILE, "NULL")
7204928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .build();
7214928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
722f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    /** Contains just the contacts vCard columns */
723f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsVCardProjectionMap = ProjectionMap.builder()
724fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            .add(Contacts._ID)
725f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.DISPLAY_NAME, Contacts.DISPLAY_NAME + " || '.vcf'")
726f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.SIZE, "NULL")
727f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
728f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
729ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    /** Contains just the raw contacts columns */
730f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactsProjectionMap = ProjectionMap.builder()
731f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
732f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
733f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
734f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_PRIMARY)
735f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_ALTERNATIVE)
736f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_SOURCE)
737f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME)
738f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME_STYLE)
739f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_PRIMARY)
740f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_ALTERNATIVE)
741f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.TIMES_CONTACTED)
742f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.LAST_TIME_CONTACTED)
743f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CUSTOM_RINGTONE)
744f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SEND_TO_VOICEMAIL)
745f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
746f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.AGGREGATION_MODE)
74724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
748f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
749f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
750f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
751f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
752a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the raw entity view*/
753f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawEntityProjectionMap = ProjectionMap.builder()
754f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
755f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
756f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.Entity.DATA_ID)
757f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
758f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
75924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
760f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
761f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
762f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
763f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
764f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
765a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the contact entity view*/
766f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sEntityProjectionMap = ProjectionMap.builder()
767f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity._ID)
768f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.CONTACT_ID)
769f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.RAW_CONTACT_ID)
770f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DATA_ID)
771f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.NAME_RAW_CONTACT_ID)
772f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DELETED)
77324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(Contacts.IS_USER_PROFILE)
774f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
775f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
776f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
777f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
778f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
779f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
780f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
781f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
7824a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /** Contains columns from the data view */
783f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataProjectionMap = ProjectionMap.builder()
784f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID)
785f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RAW_CONTACT_ID)
786f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CONTACT_ID)
787f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.NAME_RAW_CONTACT_ID)
78824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
789f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
790f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
791f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
792f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
793f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
794f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
795f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
7965e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /** Contains columns from the data view */
797f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDistinctDataProjectionMap = ProjectionMap.builder()
798f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID, "MIN(" + Data._ID + ")")
799f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
80024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
801f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
802f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
803f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
804f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
805f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
806f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
8079261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
808f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sPhoneLookupProjectionMap = ProjectionMap.builder()
809f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup._ID, "contacts_view." + Contacts._ID)
810f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LOOKUP_KEY, "contacts_view." + Contacts.LOOKUP_KEY)
811f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.DISPLAY_NAME, "contacts_view." + Contacts.DISPLAY_NAME)
812f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LAST_TIME_CONTACTED, "contacts_view." + Contacts.LAST_TIME_CONTACTED)
813f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TIMES_CONTACTED, "contacts_view." + Contacts.TIMES_CONTACTED)
814f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.STARRED, "contacts_view." + Contacts.STARRED)
815f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.IN_VISIBLE_GROUP, "contacts_view." + Contacts.IN_VISIBLE_GROUP)
816f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.PHOTO_ID, "contacts_view." + Contacts.PHOTO_ID)
8173d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_URI, "contacts_view." + Contacts.PHOTO_URI)
8183d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_THUMBNAIL_URI, "contacts_view." + Contacts.PHOTO_THUMBNAIL_URI)
819f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.CUSTOM_RINGTONE, "contacts_view." + Contacts.CUSTOM_RINGTONE)
820f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.HAS_PHONE_NUMBER, "contacts_view." + Contacts.HAS_PHONE_NUMBER)
821f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.SEND_TO_VOICEMAIL, "contacts_view." + Contacts.SEND_TO_VOICEMAIL)
822f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.NUMBER, Phone.NUMBER)
823f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TYPE, Phone.TYPE)
824f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LABEL, Phone.LABEL)
8252530512f639c4979fd7371c7dd25dd67e8118124Bai Tao            .add(PhoneLookup.NORMALIZED_NUMBER, Phone.NORMALIZED_NUMBER)
826f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
827f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
828ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
829f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsProjectionMap = ProjectionMap.builder()
830f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups._ID)
831f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_NAME)
832f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_TYPE)
83343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(Groups.DATA_SET)
83443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(Groups.ACCOUNT_TYPE_AND_DATA_SET)
835f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SOURCE_ID)
836f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DIRTY)
837f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.VERSION)
838f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.RES_PACKAGE)
839f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE)
840f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE_RES)
841f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.GROUP_VISIBLE)
842f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYSTEM_ID)
843f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DELETED)
844f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.NOTES)
845f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SHOULD_SYNC)
846f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.FAVORITES)
847f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.AUTO_ADD)
848c039cfb78c40730483fd71178df63ada5826a315Dmitri Plotnikov            .add(Groups.GROUP_IS_READ_ONLY)
849f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC1)
850f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC2)
851f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC3)
852f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC4)
853f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
854f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
855ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains {@link Groups} columns along with summary details */
856f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsSummaryProjectionMap = ProjectionMap.builder()
857f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sGroupsProjectionMap)
858f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_COUNT,
859f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                    "(SELECT COUNT(" + ContactsColumns.CONCRETE_ID + ") FROM "
860f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + Tables.CONTACTS_JOIN_RAW_CONTACTS_DATA_FILTERED_BY_GROUPMEMBERSHIP
861f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + ")")
862f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_WITH_PHONES,
863f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                    "(SELECT COUNT(" + ContactsColumns.CONCRETE_ID + ") FROM "
864f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + Tables.CONTACTS_JOIN_RAW_CONTACTS_DATA_FILTERED_BY_GROUPMEMBERSHIP
865f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + " WHERE " + Contacts.HAS_PHONE_NUMBER + ")")
866f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            .build();
867f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa
868f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa    // This is only exposed as hidden API for the contacts app, so we can be very specific in
869f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa    // the filtering
870f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa    private static final ProjectionMap sGroupsSummaryProjectionMapWithGroupCountPerAccount =
871f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            ProjectionMap.builder()
872f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            .addAll(sGroupsSummaryProjectionMap)
873f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            .add(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT,
874f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                    "(SELECT COUNT(*) FROM " + Views.GROUPS + " WHERE "
875f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + "(" + Groups.ACCOUNT_NAME + "="
876f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + GroupsColumns.CONCRETE_ACCOUNT_NAME
877f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + " AND "
878f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.ACCOUNT_TYPE + "=" + GroupsColumns.CONCRETE_ACCOUNT_TYPE
879f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + " AND "
880f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.DELETED + "=0 AND "
881f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.FAVORITES + "=0 AND "
882f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.AUTO_ADD + "=0"
883f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + ")"
884f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + " GROUP BY "
885f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.ACCOUNT_NAME + ", " + Groups.ACCOUNT_TYPE
886f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                   + ")")
887f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
888f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
889373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains the agg_exceptions columns */
890f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sAggregationExceptionsProjectionMap = ProjectionMap.builder()
891f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id")
892f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.TYPE)
893f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID1)
894f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID2)
895f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
896f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
897eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    /** Contains the agg_exceptions columns */
898f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSettingsProjectionMap = ProjectionMap.builder()
899f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_NAME)
900f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_TYPE)
901f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_VISIBLE)
902f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.SHOULD_SYNC)
903f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ANY_UNSYNCED,
904f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN MIN(" + Settings.SHOULD_SYNC
905f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                        + ",(SELECT "
906f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + "(CASE WHEN MIN(" + Groups.SHOULD_SYNC + ") IS NULL"
907f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " THEN 1"
908f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " ELSE MIN(" + Groups.SHOULD_SYNC + ")"
909f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " END)"
910f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.GROUPS
911f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
912f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_NAME
913f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
914f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_TYPE + "))=0"
915f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN 1"
916f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE 0"
917f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " END)")
918f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_COUNT,
919f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
920f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
921f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
922f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
923f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
924f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
925f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_WITH_PHONES,
926f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
927f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
928f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
929f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + Contacts.HAS_PHONE_NUMBER
930f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
931f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
932f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
933f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
934f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
93582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Contains StatusUpdates columns */
936f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStatusUpdatesProjectionMap = ProjectionMap.builder()
937f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PresenceColumns.RAW_CONTACT_ID)
938f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.DATA_ID, DataColumns.CONCRETE_ID)
939f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_ACCOUNT)
940f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_HANDLE)
941f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PROTOCOL)
942f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // We cannot allow a null in the custom protocol field, because SQLite3 does not
943f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // properly enforce uniqueness of null values
944f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CUSTOM_PROTOCOL,
945f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN " + StatusUpdates.CUSTOM_PROTOCOL + "=''"
946f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN NULL"
947f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE " + StatusUpdates.CUSTOM_PROTOCOL + " END)")
948f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PRESENCE)
949f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CHAT_CAPABILITY)
950f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS)
951f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_TIMESTAMP)
952f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_RES_PACKAGE)
953f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_ICON)
954f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_LABEL)
955f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
956f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
9573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /** Contains StreamItems columns */
9583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final ProjectionMap sStreamItemsProjectionMap = ProjectionMap.builder()
9593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems._ID, StreamItemsColumns.CONCRETE_ID)
9603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(RawContacts.CONTACT_ID)
9610bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.ACCOUNT_NAME, RawContactsColumns.CONCRETE_ACCOUNT_NAME)
9620bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.ACCOUNT_TYPE, RawContactsColumns.CONCRETE_ACCOUNT_TYPE)
9630bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.DATA_SET, RawContactsColumns.CONCRETE_DATA_SET)
9643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RAW_CONTACT_ID)
9650bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.RAW_CONTACT_SOURCE_ID, RawContactsColumns.CONCRETE_SOURCE_ID)
9663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_PACKAGE)
9673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_ICON)
9683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_LABEL)
9693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.TEXT)
9703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.TIMESTAMP)
9713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.COMMENTS)
9720bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.SYNC1)
9730bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.SYNC2)
9740bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.SYNC3)
9750bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.SYNC4)
9763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .build();
9773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
9783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final ProjectionMap sStreamItemPhotosProjectionMap = ProjectionMap.builder()
9793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos._ID, StreamItemPhotosColumns.CONCRETE_ID)
9803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RAW_CONTACT_ID)
9810bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.RAW_CONTACT_SOURCE_ID, RawContactsColumns.CONCRETE_SOURCE_ID)
9823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.STREAM_ITEM_ID)
9833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.SORT_INDEX)
9846802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            .add(StreamItemPhotos.PHOTO_FILE_ID)
9856802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            .add(StreamItemPhotos.PHOTO_URI,
9866802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    "'" + DisplayPhoto.CONTENT_URI + "'||'/'||" + StreamItemPhotos.PHOTO_FILE_ID)
9871dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.HEIGHT)
9881dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.WIDTH)
9891dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.FILESIZE)
9900bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItemPhotos.SYNC1)
9910bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItemPhotos.SYNC2)
9920bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItemPhotos.SYNC3)
9930bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItemPhotos.SYNC4)
9943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .build();
9953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
9961b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    /** Contains Live Folders columns */
997f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sLiveFoldersProjectionMap = ProjectionMap.builder()
998f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders._ID, Contacts._ID)
999f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders.NAME, Contacts.DISPLAY_NAME)
1000f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // TODO: Put contact photo back when we have a way to display a default icon
1001f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // for contacts without a photo
1002f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // .add(LiveFolders.ICON_BITMAP, Photos.DATA)
1003f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
1004f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
1005d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /** Contains {@link Directory} columns */
1006f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDirectoryProjectionMap = ProjectionMap.builder()
1007f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory._ID)
1008f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.PACKAGE_NAME)
1009f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.TYPE_RESOURCE_ID)
1010f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DISPLAY_NAME)
1011f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DIRECTORY_AUTHORITY)
1012f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_TYPE)
1013f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_NAME)
1014f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.EXPORT_SUPPORT)
1015778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.SHORTCUT_SUPPORT)
1016778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.PHOTO_SUPPORT)
1017f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
10187e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
10199705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    // where clause to update the status_updates table
10209705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private static final String WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE =
10219705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdatesColumns.DATA_ID + " IN (SELECT Distinct " + StatusUpdates.DATA_ID +
10229705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " FROM " + Tables.STATUS_UPDATES + " LEFT OUTER JOIN " + Tables.PRESENCE +
10239705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " ON " + StatusUpdatesColumns.DATA_ID + " = " + StatusUpdates.DATA_ID + " WHERE ";
10249705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
10252526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private static final String[] EMPTY_STRING_ARRAY = new String[0];
10262526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
1027bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1028bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Notification ID for failure to import contacts.
1029bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1030bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private static final int LEGACY_IMPORT_FAILED_NOTIFICATION = 1;
103151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
103203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_START_MATCH = "[";
103303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_END_MATCH = "]";
103403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_ELLIPSIS = "...";
103503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final int DEFAULT_SNIPPET_ARG_MAX_TOKENS = -10;
103603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
10379a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    private boolean sIsPhoneInitialized;
10389a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    private boolean sIsPhone;
10399a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
1040f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private StringBuilder mSb = new StringBuilder();
10411129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs1 = new String[1];
10421129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs2 = new String[2];
10432526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private ArrayList<String> mSelectionArgs = Lists.newArrayList();
10442526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
1045f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private Account mAccount;
1046f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
104746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
104846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Stores mapping from type Strings exposed via {@link DataUsageFeedback} to
104946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * type integers in {@link DataUsageStatColumns}.
105046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
105146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final Map<String, Integer> sDataUsageTypeMap;
105246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
10534f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
10544f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
1055a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
1056d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
1057d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
1058a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_ID_DATA);
1059a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/entities", CONTACTS_ID_ENTITIES);
10603653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
10613653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
10622d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*",
10632d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                AGGREGATION_SUGGESTIONS);
1064a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_ID_PHOTO);
1065f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/display_photo",
1066f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_ID_DISPLAY_PHOTO);
10673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/stream_items",
10683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_ID_STREAM_ITEMS);
1069c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter", CONTACTS_FILTER);
10705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER);
10715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP);
10722149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/data", CONTACTS_LOOKUP_DATA);
10735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
10742149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/data",
10752149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                CONTACTS_LOOKUP_ID_DATA);
1076f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/display_photo",
1077f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_LOOKUP_DISPLAY_PHOTO);
1078f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/display_photo",
1079f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_LOOKUP_ID_DISPLAY_PHOTO);
1080a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/entities",
1081a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ENTITIES);
1082a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/entities",
1083a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ID_ENTITIES);
10843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/stream_items",
10853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_LOOKUP_STREAM_ITEMS);
10863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/stream_items",
10873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_LOOKUP_ID_STREAM_ITEMS);
1088f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_vcard/*", CONTACTS_AS_VCARD);
108942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_multi_vcard/*",
109042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                CONTACTS_AS_MULTI_VCARD);
10915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT);
1092ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*",
1093ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                CONTACTS_STREQUENT_FILTER);
10945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP);
109545ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa        matcher.addURI(ContactsContract.AUTHORITY, "contacts/frequent", CONTACTS_FREQUENT);
10963653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
10975ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
10985ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
10995ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA);
1100f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/display_photo",
1101f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                RAW_CONTACTS_ID_DISPLAY_PHOTO);
110246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ENTITY_ID);
11033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/stream_items",
11043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                RAW_CONTACTS_ID_STREAM_ITEMS);
110546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
110646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contact_entities", RAW_CONTACT_ENTITIES);
1107b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
11084f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
11094f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
1110ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
111148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID);
11125e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER);
1113ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
11144a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS);
111548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID);
11161dac83b8fa58944acfd00f44e717a7dddc659d2dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup", EMAILS_LOOKUP);
11175e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP);
11185e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER);
11194a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
1120ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
112148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
112246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        /** "*" is in CSV form with data ids ("123,456,789") */
112346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        matcher.addURI(ContactsContract.AUTHORITY, "data/usagefeedback/*", DATA_USAGE_FEEDBACK_ID);
11241f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1125ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
1126ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
1127ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
1128ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
112935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
1130b5a4add17815167d20a90645779df34cdf45280dFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#",
1131b5a4add17815167d20a90645779df34cdf45280dFred Quintana                SYNCSTATE_ID);
113235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1133a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
1134b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
1135b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
1136b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
1137b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
11384f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1139eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS);
1140eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
114182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates", STATUS_UPDATES);
114282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates/#", STATUS_UPDATES_ID);
11431f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1144c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
1145c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
1146c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
1147c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
11482d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*",
1149c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SHORTCUT);
1150c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
11511b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts",
11521b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS);
11531b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts/*",
11541b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_GROUP_NAME);
11551b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts_with_phones",
11561b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_WITH_PHONES);
11571b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/favorites",
11581b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_FAVORITES);
115909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
116009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "provider_status", PROVIDER_STATUS);
1161d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
1162d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories", DIRECTORIES);
1163d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories/#", DIRECTORIES_ID);
11647a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
11657a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "complete_name", COMPLETE_NAME);
116624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
116724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile", PROFILE);
116824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/entities", PROFILE_ENTITIES);
116924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/data", PROFILE_DATA);
117024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/data/#", PROFILE_DATA_ID);
117124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/as_vcard", PROFILE_AS_VCARD);
117224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts", PROFILE_RAW_CONTACTS);
117324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#",
117424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID);
117524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/data",
117624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID_DATA);
117724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/entity",
117824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID_ENTITIES);
117946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
11803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items", STREAM_ITEMS);
11813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/photo", STREAM_ITEMS_PHOTOS);
11823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#", STREAM_ITEMS_ID);
11833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#/photo", STREAM_ITEMS_ID_PHOTOS);
11843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#/photo/#",
11853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                STREAM_ITEMS_ID_PHOTOS_ID);
11863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items_limit", STREAM_ITEMS_LIMIT);
11873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
1188f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "display_photo/*", DISPLAY_PHOTO);
1189f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "photo_dimensions", PHOTO_DIMENSIONS);
1190f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
119146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        HashMap<String, Integer> tmpTypeMap = new HashMap<String, Integer>();
119246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_CALL, DataUsageStatColumns.USAGE_TYPE_INT_CALL);
119346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_LONG_TEXT,
119446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT);
119546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_SHORT_TEXT,
119646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DataUsageStatColumns.USAGE_TYPE_INT_SHORT_TEXT);
119746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        sDataUsageTypeMap = Collections.unmodifiableMap(tmpTypeMap);
119819a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    }
119919a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
1200d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static class DirectoryInfo {
1201d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String authority;
1202d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountName;
1203d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountType;
1204d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
1205d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
1206d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
1207d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Cached information about contact directories.
1208d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
12094458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private HashMap<String, DirectoryInfo> mDirectoryCache = new HashMap<String, DirectoryInfo>();
12104458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private boolean mDirectoryCacheValid = false;
1211d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
12123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
121343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * An entry in group id cache. It maps the combination of (account type, account name, data set,
1214ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * and source id) to group row id.
1215ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     */
1216e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    public static class GroupIdCacheEntry {
1217ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType;
1218ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName;
121943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String dataSet;
1220ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String sourceId;
1221ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        long groupId;
1222ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    }
1223a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1224e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // We don't need a soft cache for groups - the assumption is that there will only
1225e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // be a small number of contact groups. The cache is keyed off source id.  The value
1226e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // is a list of groups with this group id.
1227e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    private HashMap<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache = Maps.newHashMap();
1228e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov
122924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
123024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Cached information about the contact ID and raw contact IDs that make up the user's
123124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * profile entry.
123224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
123324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static class ProfileIdCache {
123424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        boolean inited;
123524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        long profileContactId;
123624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        Set<Long> profileRawContactIds = Sets.newHashSet();
123724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        Set<Long> profileDataIds = Sets.newHashSet();
123824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
123924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        /**
124024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         * Initializes the cache of profile contact and raw contact IDs.  Does nothing if
124124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         * the cache is already initialized (unless forceRefresh is set to true).
124224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         * @param db The contacts database.
124324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         * @param forceRefresh Whether to force re-initialization of the cache.
124424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         */
124524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        private void init(SQLiteDatabase db, boolean forceRefresh) {
124624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            if (!inited || forceRefresh) {
124724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                profileContactId = 0;
124824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                profileRawContactIds.clear();
124924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                profileDataIds.clear();
125024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                Cursor c = db.rawQuery("SELECT " +
125124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        RawContactsColumns.CONCRETE_CONTACT_ID + "," +
125224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        RawContactsColumns.CONCRETE_ID + "," +
125324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        DataColumns.CONCRETE_ID +
125424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        " FROM " + Tables.RAW_CONTACTS + " JOIN " + Tables.ACCOUNTS + " ON " +
125524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        RawContactsColumns.CONCRETE_ID + "=" +
125624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        AccountsColumns.PROFILE_RAW_CONTACT_ID +
125724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        " JOIN " + Tables.DATA + " ON " +
125824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        RawContactsColumns.CONCRETE_ID + "=" + DataColumns.CONCRETE_RAW_CONTACT_ID,
125924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        null);
126024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                try {
126124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    while (c.moveToNext()) {
126224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        if (profileContactId == 0) {
126324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                            profileContactId = c.getLong(0);
126424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        }
126524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        profileRawContactIds.add(c.getLong(1));
126624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        profileDataIds.add(c.getLong(2));
126724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    }
126824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                } finally {
126924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    c.close();
127024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                }
127124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
127224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
127324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
127424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
127524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private ProfileIdCache mProfileIdCache;
127624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1277f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
1278f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Maximum dimension (height or width) of display photos.  Larger images will be scaled
1279f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * to fit.
1280f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
1281f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private int mMaxDisplayPhotoDim;
1282f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1283f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
1284f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Maximum dimension (height or width) of photo thumbnails.
1285f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
1286f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private int mMaxThumbnailPhotoDim;
1287f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
12883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private HashMap<String, DataRowHandler> mDataRowHandlers;
1289b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private ContactsDatabaseHelper mDbHelper;
129031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
1291f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private PhotoStore mPhotoStore;
1292f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
12934097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
1294f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private NameLookupBuilder mNameLookupBuilder;
1295315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
1296622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private PostalSplitter mPostalSplitter;
1297622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
129872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    private ContactDirectoryManager mContactDirectoryManager;
1299622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private ContactAggregator mContactAggregator;
1300f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
1301a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    private GlobalSearchSupport mGlobalSearchSupport;
1302d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov    private CommonNicknameCache mCommonNicknameCache;
1303f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    private SearchIndexManager mSearchIndexManager;
1304a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
130520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private ContentValues mValues = new ContentValues();
130673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    private HashMap<String, Boolean> mAccountWritability = Maps.newHashMap();
130720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
130809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private int mProviderStatus = ProviderStatus.STATUS_NORMAL;
13093826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private boolean mProviderStatusUpdateNeeded;
131009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private long mEstimatedStorageRequirement = 0;
131115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private volatile CountDownLatch mReadAccessLatch;
131215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private volatile CountDownLatch mWriteAccessLatch;
131315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private boolean mAccountUpdateListenerRegistered;
1314bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private boolean mOkToOpenAccess = true;
131573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
1316d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov    private TransactionContext mTransactionContext = new TransactionContext();
1317de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov
13181a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey    private boolean mVisibleTouched = false;
13191a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
132081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    private boolean mSyncToNetwork;
132181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
13224cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    private Locale mCurrentLocale;
13233826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private int mContactsAccountCount;
1324d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov
1325bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private HandlerThread mBackgroundThread;
1326bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private Handler mBackgroundHandler;
1327bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1328f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private long mLastPhotoCleanup = 0;
1329f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
13304f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
13314f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
1332de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        super.onCreate();
1333ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        try {
1334ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return initialize();
1335ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        } catch (RuntimeException e) {
1336ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            Log.e(TAG, "Cannot start provider", e);
1337ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return false;
1338ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        }
1339ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    }
134035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1341ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    private boolean initialize() {
134215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        StrictMode.setThreadPolicy(
134315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
134415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
13453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Resources resources = getContext().getResources();
1346f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mMaxDisplayPhotoDim = resources.getInteger(
1347f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                R.integer.config_max_display_photo_dim);
1348f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mMaxThumbnailPhotoDim = resources.getInteger(
1349f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                R.integer.config_max_thumbnail_photo_dim);
13503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
135124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        mProfileIdCache = new ProfileIdCache();
1352b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper = (ContactsDatabaseHelper)getDatabaseHelper();
135372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        mContactDirectoryManager = new ContactDirectoryManager(this);
1354a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mGlobalSearchSupport = new GlobalSearchSupport(this);
135565ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov
1356bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        // The provider is closed for business until fully initialized
135715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mReadAccessLatch = new CountDownLatch(1);
135815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mWriteAccessLatch = new CountDownLatch(1);
135972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
1360bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundThread = new HandlerThread("ContactsProviderWorker",
1361bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                Process.THREAD_PRIORITY_BACKGROUND);
1362bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundThread.start();
1363bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler = new Handler(mBackgroundThread.getLooper()) {
1364bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            @Override
1365bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            public void handleMessage(Message msg) {
1366bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                performBackgroundTask(msg.what, msg.obj);
1367bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1368bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        };
13692a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov
137015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_INITIALIZE);
1371bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS);
1372bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
1373bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_LOCALE);
1374bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM);
137505e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_SEARCH_INDEX);
1376bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_PROVIDER_STATUS);
137715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_OPEN_WRITE_ACCESS);
1378f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
13793826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
138049d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov        return true;
13814f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
13824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1383767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    /**
138451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * (Re)allocates all locale-sensitive structures.
138551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
138604b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov    private void initForDefaultLocale() {
138715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        Context context = getContext();
138815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mLegacyApiSupport = new LegacyApiSupport(context, mDbHelper, this, mGlobalSearchSupport);
13894cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mCurrentLocale = getLocale();
139004b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov        mNameSplitter = mDbHelper.createNameSplitter();
13914cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
13924cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mPostalSplitter = new PostalSplitter(mCurrentLocale);
139351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        mCommonNicknameCache = new CommonNicknameCache(mDbHelper.getReadableDatabase());
1394cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao        ContactLocaleUtils.getIntance().setLocale(mCurrentLocale);
13955b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator = new ContactAggregator(this, mDbHelper,
139615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                createPhotoPriorityResolver(context), mNameSplitter, mCommonNicknameCache);
13975b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
1398f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov        mSearchIndexManager = new SearchIndexManager(this);
139924bc499d6756e7b2bd95b2eabb64a6d9d13435baDave Santoro        mPhotoStore = new PhotoStore(getContext().getFilesDir(), mDbHelper);
14005b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
1401bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers = new HashMap<String, DataRowHandler>();
1402bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1403bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Email.CONTENT_ITEM_TYPE,
14046d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForEmail(context, mDbHelper, mContactAggregator));
1405bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Im.CONTENT_ITEM_TYPE,
14066d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForIm(context, mDbHelper, mContactAggregator));
1407bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Organization.CONTENT_ITEM_TYPE,
14086d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForOrganization(context, mDbHelper, mContactAggregator));
1409bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Phone.CONTENT_ITEM_TYPE,
14106d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForPhoneNumber(context, mDbHelper, mContactAggregator));
1411bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE,
14126d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForNickname(context, mDbHelper, mContactAggregator));
1413bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(StructuredName.CONTENT_ITEM_TYPE,
14146d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForStructuredName(context, mDbHelper, mContactAggregator,
1415bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mNameSplitter, mNameLookupBuilder));
1416bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(StructuredPostal.CONTENT_ITEM_TYPE,
14176d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForStructuredPostal(context, mDbHelper, mContactAggregator,
1418bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mPostalSplitter));
1419bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(GroupMembership.CONTENT_ITEM_TYPE,
14206d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForGroupMembership(context, mDbHelper, mContactAggregator,
1421bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mGroupIdCache));
1422bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Photo.CONTENT_ITEM_TYPE,
1423f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                new DataRowHandlerForPhoto(context, mDbHelper, mContactAggregator, mPhotoStore));
14246d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov        mDataRowHandlers.put(Note.CONTENT_ITEM_TYPE,
14256d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForNote(context, mDbHelper, mContactAggregator));
1426bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1427bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1428bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    /**
1429bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     * Visible for testing.
1430bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     */
1431bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    /* package */ PhotoPriorityResolver createPhotoPriorityResolver(Context context) {
1432bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        return new PhotoPriorityResolver(context);
1433bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1434bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1435bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task) {
1436bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendEmptyMessage(task);
1437bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1438bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1439bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task, Object arg) {
1440bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendMessage(mBackgroundHandler.obtainMessage(task, arg));
1441bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1442bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1443bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void performBackgroundTask(int task, Object arg) {
1444bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        switch (task) {
144515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_INITIALIZE: {
144615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                initForDefaultLocale();
144715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch.countDown();
144815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch = null;
144915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                break;
145015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            }
145115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
145215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_OPEN_WRITE_ACCESS: {
1453bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (mOkToOpenAccess) {
145415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch.countDown();
145515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch = null;
1456bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1457bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1458bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1459bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1460bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS: {
1461bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (isLegacyContactImportNeeded()) {
1462bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    importLegacyContactsInBackground();
1463bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1464bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1465bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1466bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1467bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_ACCOUNTS: {
146815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Context context = getContext();
146915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                if (!mAccountUpdateListenerRegistered) {
147015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    AccountManager.get(context).addOnAccountsUpdatedListener(this, null, false);
147115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mAccountUpdateListenerRegistered = true;
147215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                }
147315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
147415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Account[] accounts = AccountManager.get(context).getAccounts();
1475bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                boolean accountsChanged = updateAccountsInBackground(accounts);
1476bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateContactsAccountCount(accounts);
1477bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateDirectoriesInBackground(accountsChanged);
1478bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1479bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1480bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1481bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_LOCALE: {
1482bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateLocaleInBackground();
1483bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1484bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1485bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1486fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            case BACKGROUND_TASK_CHANGE_LOCALE: {
1487fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                changeLocaleInBackground();
1488fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                break;
1489fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            }
1490fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1491bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM: {
1492bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (isAggregationUpgradeNeeded()) {
1493bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    upgradeAggregationAlgorithmInBackground();
1494bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1495bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1496bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1497bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
149805e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_SEARCH_INDEX: {
149905e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                updateSearchIndexInBackground();
150005e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                break;
150105e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            }
150205e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
1503bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_PROVIDER_STATUS: {
1504bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateProviderStatus();
1505bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1506bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1507bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1508bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_DIRECTORIES: {
1509bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (arg != null) {
1510bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    mContactDirectoryManager.onPackageChanged((String) arg);
1511bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1512bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1513bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1514f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1515f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case BACKGROUND_TASK_CLEANUP_PHOTOS: {
1516f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Check rate limit.
1517f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long now = System.currentTimeMillis();
1518f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (now - mLastPhotoCleanup > PHOTO_CLEANUP_RATE_LIMIT) {
1519f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    mLastPhotoCleanup = now;
1520f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    cleanupPhotoStore();
1521f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    break;
1522f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
1523f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1524bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
15254cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
15264cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao
152753fac8f99f3884c372c907a76766d27fa9e1d95fDmitri Plotnikov    public void onLocaleChanged() {
15283826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
15293826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
15304f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov            return;
15314f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov        }
15324f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov
1533fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_CHANGE_LOCALE);
15344cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
153551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
153651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    /**
153751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * Verifies that the contacts database is properly configured for the current locale.
153851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * If not, changes the database locale to the current locale using an asynchronous task.
153951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * This needs to be done asynchronously because the process involves rebuilding
154051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * large data structures (name lookup, sort keys), which can take minutes on
154151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * a large set of contacts.
154251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
1543bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateLocaleInBackground() {
1544f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
1545f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        // The process is already running - postpone the change
1546f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        if (mProviderStatus == ProviderStatus.STATUS_CHANGING_LOCALE) {
1547f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov            return;
1548f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        }
1549f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
155051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
155151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final String providerLocale = prefs.getString(PREF_LOCALE, null);
155251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final Locale currentLocale = mCurrentLocale;
155351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        if (currentLocale.toString().equals(providerLocale)) {
155451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            return;
155551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        }
155651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
155751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        int providerStatus = mProviderStatus;
155851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_CHANGING_LOCALE);
1559bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDbHelper.setLocale(this, currentLocale);
1560bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        prefs.edit().putString(PREF_LOCALE, currentLocale.toString()).apply();
1561bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        setProviderStatus(providerStatus);
1562bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
156351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
1564fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    /**
1565fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     * Reinitializes the provider for a new locale.
1566fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     */
1567fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    private void changeLocaleInBackground() {
1568fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Re-initializing the provider without stopping it.
1569fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Locking the database will prevent inserts/updates/deletes from
1570fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // running at the same time, but queries may still be running
1571fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // on other threads. Those queries may return inconsistent results.
1572fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        SQLiteDatabase db = mDbHelper.getWritableDatabase();
1573fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        db.beginTransaction();
1574fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        try {
1575fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            initForDefaultLocale();
1576fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.setTransactionSuccessful();
1577fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        } finally {
1578fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.endTransaction();
1579fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        }
1580fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1581fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        updateLocaleInBackground();
1582fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    }
1583fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
158405e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    protected void updateSearchIndexInBackground() {
158505e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        mSearchIndexManager.updateIndex();
158605e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    }
158705e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
1588bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateDirectoriesInBackground(boolean rescan) {
1589bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mContactDirectoryManager.scanAllPackages(rescan);
159051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
159151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
15923826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateProviderStatus() {
15933826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
15943826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
15953826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return;
15963826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
15973826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
15983e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson        // No accounts/no contacts status is true if there are no account and
15993e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson        // there are
16003e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson        // no contacts or one profile contact
16013e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson        if (mContactsAccountCount == 0) {
16023e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson            long contactsNum = DatabaseUtils.queryNumEntries(mDbHelper.getReadableDatabase(),
16033e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                    Tables.CONTACTS, null);
16043e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson            if (contactsNum == 0) {
16053e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                setProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS);
16063e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson            } else if (contactsNum == 1) {
16073e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                // if we have one contact, need to make sure it is the local
16083e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                // profile
16093e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                // so need to get the raw_contact id from the account table and
16103e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                // then
16113e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                // make sure the raw_contacts exists and it is not deleted
16123e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                long rawId = DatabaseUtils.longForQuery(
16133e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                            mDbHelper.getReadableDatabase(),
16143e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                            "SELECT " + AccountsColumns.PROFILE_RAW_CONTACT_ID +
16153e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                                    " FROM " + Tables.ACCOUNTS
16163e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                            , null);
16173e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                if (rawId == 0) {
16183e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                    setProviderStatus(ProviderStatus.STATUS_NORMAL);
16193e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                    return;
16203e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                }
16213e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                boolean deleted = DatabaseUtils.longForQuery(
16223e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                        mDbHelper.getReadableDatabase(),
16233e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                        "SELECT " + RawContacts.DELETED +
16243e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                                " FROM " + Tables.RAW_CONTACTS +
16253e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                                " WHERE " + RawContacts._ID + "=" + String.valueOf(rawId),
16263e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                                null) == 1;
16273e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                if (deleted) {
16283e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                    setProviderStatus(ProviderStatus.STATUS_NORMAL);
16293e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                    return;
16303e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                }
16313e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                setProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS);
16323e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson            } else {
16333e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                setProviderStatus(ProviderStatus.STATUS_NORMAL);
16343e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson            }
16353826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } else {
16363826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            setProviderStatus(ProviderStatus.STATUS_NORMAL);
16373826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
16383826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
16393826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
164031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
1641f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    protected void cleanupPhotoStore() {
16426802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        SQLiteDatabase db = mDbHelper.getWritableDatabase();
16436802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
16446802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Assemble the set of photo store file IDs that are in use, and send those to the photo
1645f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // store.  Any photos that aren't in that set will be deleted, and any photos that no
1646f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // longer exist in the photo store will be returned for us to clear out in the DB.
16476802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Cursor c = db.query(Views.DATA, new String[]{Data._ID, Photo.PHOTO_FILE_ID},
1648f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                Data.MIMETYPE + "=" + Photo.MIMETYPE + " AND "
1649f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        + Photo.PHOTO_FILE_ID + " IS NOT NULL", null, null, null, null);
16506802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Set<Long> usedPhotoFileIds = Sets.newHashSet();
16516802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> photoFileIdToDataId = Maps.newHashMap();
1652f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        try {
1653f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            while (c.moveToNext()) {
16546802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long dataId = c.getLong(0);
16556802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long photoFileId = c.getLong(1);
16566802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                usedPhotoFileIds.add(photoFileId);
16576802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                photoFileIdToDataId.put(photoFileId, dataId);
16586802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
16596802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        } finally {
16606802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            c.close();
16616802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
16626802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
16636802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Also query for all social stream item photos.
16646802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        c = db.query(Tables.STREAM_ITEM_PHOTOS,
16656802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                new String[]{
16666802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        StreamItemPhotos._ID,
16676802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        StreamItemPhotos.STREAM_ITEM_ID,
16686802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        StreamItemPhotos.PHOTO_FILE_ID
16696802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                },
16706802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                null, null, null, null, null);
16716802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> photoFileIdToStreamItemPhotoId = Maps.newHashMap();
16726802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> streamItemPhotoIdToStreamItemId = Maps.newHashMap();
16736802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        try {
16746802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            while (c.moveToNext()) {
16756802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long streamItemPhotoId = c.getLong(0);
16766802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long streamItemId = c.getLong(1);
16776802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long photoFileId = c.getLong(2);
16786802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                usedPhotoFileIds.add(photoFileId);
16796802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                photoFileIdToStreamItemPhotoId.put(photoFileId, streamItemPhotoId);
16806802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                streamItemPhotoIdToStreamItemId.put(streamItemPhotoId, streamItemId);
1681f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1682f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } finally {
1683f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            c.close();
1684f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
1685f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1686f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // Run the photo store cleanup.
16876802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Set<Long> missingPhotoIds = mPhotoStore.cleanup(usedPhotoFileIds);
1688f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1689f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // If any of the keys we're using no longer exist, clean them up.
16906802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (!missingPhotoIds.isEmpty()) {
1691f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            ArrayList<ContentProviderOperation> ops = Lists.newArrayList();
16926802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            for (long missingPhotoId : missingPhotoIds) {
16936802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (photoFileIdToDataId.containsKey(missingPhotoId)) {
16946802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    long dataId = photoFileIdToDataId.get(missingPhotoId);
1695f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    ContentValues updateValues = new ContentValues();
1696f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    updateValues.putNull(Photo.PHOTO_FILE_ID);
1697f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    ops.add(ContentProviderOperation.newUpdate(
16986802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            ContentUris.withAppendedId(Data.CONTENT_URI, dataId))
1699f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            .withValues(updateValues).build());
1700f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
17016802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (photoFileIdToStreamItemPhotoId.containsKey(missingPhotoId)) {
17026802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // For missing photos that were in stream item photos, just delete the stream
17036802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // item photo.
17046802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    long streamItemPhotoId = photoFileIdToStreamItemPhotoId.get(missingPhotoId);
17056802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    long streamItemId = streamItemPhotoIdToStreamItemId.get(streamItemPhotoId);
17066802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    ops.add(ContentProviderOperation.newDelete(
17076802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            StreamItems.CONTENT_URI.buildUpon()
17086802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    .appendPath(String.valueOf(streamItemId))
17096802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    .appendPath(StreamItems.StreamItemPhotos.CONTENT_DIRECTORY)
17106802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    .appendPath(String.valueOf(streamItemPhotoId))
17116802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    .build()).build());
17126802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                }
1713f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1714f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            try {
1715f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                applyBatch(ops);
1716f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            } catch (OperationApplicationException oae) {
1717f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Not a fatal problem (and we'll try again on the next cleanup).
1718f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                Log.e(TAG, "Failed to clean up outdated photo references", oae);
1719f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1720f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
1721f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
1722f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1723f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /* Visible for testing */
1724de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    @Override
1725b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
1726b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        return ContactsDatabaseHelper.getInstance(context);
172731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
172831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
1729524913c66ce75ca8dec127ac88e3bc2249c246d9Dave Santoro    @VisibleForTesting
1730f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /* package */ PhotoStore getPhotoStore() {
1731f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        return mPhotoStore;
1732f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
1733f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
173487614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    /* package */ int getMaxDisplayPhotoDim() {
173587614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro        return mMaxDisplayPhotoDim;
173687614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    }
173787614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro
173887614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    /* package */ int getMaxThumbnailPhotoDim() {
173987614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro        return mMaxThumbnailPhotoDim;
174087614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    }
174187614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro
1742013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    /* package */ NameSplitter getNameSplitter() {
1743013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        return mNameSplitter;
1744013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    }
1745013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov
17465df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    /* package */ NameLookupBuilder getNameLookupBuilder() {
17475df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov        return mNameLookupBuilder;
17485df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    }
17495df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov
17505dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    /* Visible for testing */
1751ed78fd6df5e9f3a2d572162e5d374d1f4a625bddDmitri Plotnikov    public ContactDirectoryManager getContactDirectoryManagerForTest() {
175272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        return mContactDirectoryManager;
175372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
175472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
175572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    /* Visible for testing */
17565dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    protected Locale getLocale() {
17575dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        return Locale.getDefault();
17585dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
17595dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
17603d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    protected boolean isLegacyContactImportNeeded() {
1761b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        int version = Integer.parseInt(mDbHelper.getProperty(PROPERTY_CONTACTS_IMPORTED, "0"));
1762b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        return version < PROPERTY_CONTACTS_IMPORT_VERSION;
17633d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
17643d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1765568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected LegacyContactImporter getLegacyContactImporter() {
1766568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return new LegacyContactImporter(getContext(), this);
1767568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1768568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1769568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
1770bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     * Imports legacy contacts as a background task.
1771568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
1772bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private void importLegacyContactsInBackground() {
1773bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Importing legacy contacts");
1774bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADING);
1775568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1776bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
1777bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDbHelper.setLocale(this, mCurrentLocale);
1778bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        prefs.edit().putString(PREF_LOCALE, mCurrentLocale.toString()).commit();
1779568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1780bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        LegacyContactImporter importer = getLegacyContactImporter();
1781bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        if (importLegacyContacts(importer)) {
1782bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            onLegacyContactImportSuccess();
1783bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        } else {
1784bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            onLegacyContactImportFailure();
1785bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
1786568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1787568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1788bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1789bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Unlocks the provider and declares that the import process is complete.
1790bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1791bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportSuccess() {
1792bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
1793bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)getContext().getSystemService(Context.NOTIFICATION_SERVICE);
1794bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.cancel(LEGACY_IMPORT_FAILED_NOTIFICATION);
1795bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1796b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        // Store a property in the database indicating that the conversion process succeeded
1797b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mDbHelper.setProperty(PROPERTY_CONTACTS_IMPORTED,
1798b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov                String.valueOf(PROPERTY_CONTACTS_IMPORT_VERSION));
1799bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_NORMAL);
1800bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Completed import of legacy contacts");
1801bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    }
1802bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1803bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1804bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Announces the provider status and keeps the provider locked.
1805bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1806bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportFailure() {
1807bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Context context = getContext();
1808bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
1809bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
1810bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1811bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        // Show a notification
1812bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Notification n = new Notification(android.R.drawable.stat_notify_error,
1813bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_ticker),
1814bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                System.currentTimeMillis());
1815bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.setLatestEventInfo(context,
1816bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_title),
1817bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_text),
1818bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                PendingIntent.getActivity(context, 0, new Intent(Intents.UI.LIST_DEFAULT), 0));
1819bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
1820bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1821bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.notify(LEGACY_IMPORT_FAILED_NOTIFICATION, n);
1822bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1823bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADE_OUT_OF_MEMORY);
1824bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Failed to import legacy contacts");
1825bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1826bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        // Do not let any database changes until this issue is resolved.
1827bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mOkToOpenAccess = false;
18283d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
18293d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
18303d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /* Visible for testing */
1831568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
18320e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        boolean aggregatorEnabled = mContactAggregator.isEnabled();
18333d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mContactAggregator.setEnabled(false);
18343d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        try {
1835bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            if (importer.importContacts()) {
1836bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1837bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                // TODO aggregate all newly added raw contacts
1838bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                mContactAggregator.setEnabled(aggregatorEnabled);
1839bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                return true;
1840bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
18413d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } catch (Throwable e) {
18423d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           Log.e(TAG, "Legacy contact import failed", e);
18433d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
1844bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mEstimatedStorageRequirement = importer.getEstimatedStorageRequirement();
1845bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        return false;
18463d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
18473d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1848a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
1849a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
1850a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
1851a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
1852b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper.wipeData();
1853f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mPhotoStore.clear();
18543826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatus = ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS;
1855a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1856a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1857568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
185815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov     * During intialization, this content provider will
1859568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * block all attempts to change contacts data. In particular, it will hold
1860568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * up all contact syncs. As soon as the import process is complete, all
1861568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * processes waiting to write to the provider are unblocked and can proceed
1862568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * to compete for the database transaction monitor.
1863568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
186415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private void waitForAccess(CountDownLatch latch) {
186515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (latch == null) {
186615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            return;
186715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        }
186815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
186915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        while (true) {
187015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            try {
187115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                latch.await();
187215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                return;
187315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            } catch (InterruptedException e) {
187415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Thread.currentThread().interrupt();
1875ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            }
1876568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
1877568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1878568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1879568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1880568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
188115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1882568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.insert(uri, values);
1883568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1884568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1885568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1886568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
188715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (mWriteAccessLatch != null) {
1888bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // We are stuck trying to upgrade contacts db.  The only update request
1889bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // allowed in this case is an update of provider status, which will trigger
1890bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // an attempt to upgrade contacts again.
1891bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            int match = sUriMatcher.match(uri);
1892bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            if (match == PROVIDER_STATUS) {
1893bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                Integer newStatus = values.getAsInteger(ProviderStatus.STATUS);
1894bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                if (newStatus != null && newStatus == ProviderStatus.STATUS_UPGRADING) {
1895bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    scheduleBackgroundTask(BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS);
1896bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 1;
1897bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                } else {
1898bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 0;
1899bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                }
1900bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
1901bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
190215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1903568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.update(uri, values, selection, selectionArgs);
1904568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1905568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1906568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1907568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
190815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1909568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.delete(uri, selection, selectionArgs);
1910568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1911568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1912568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1913568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
1914568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            throws OperationApplicationException {
191515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1916568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.applyBatch(operations);
1917568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1918568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
19194f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
19207b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    public int bulkInsert(Uri uri, ContentValues[] values) {
19217b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov        waitForAccess(mWriteAccessLatch);
19227b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov        return super.bulkInsert(uri, values);
19237b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    }
19247b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov
19257b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    @Override
1926285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void onBeginTransaction() {
1927bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1928b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "onBeginTransaction");
1929b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1930285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.onBeginTransaction();
19311ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.clearPendingAggregations();
1932d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.clear();
1933b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1934b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1935285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1936285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
1937285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void beforeTransactionCommit() {
19381129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
1939bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1940b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "beforeTransactionCommit");
1941b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1942285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.beforeTransactionCommit();
1943b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
1944bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        mContactAggregator.aggregateInTransaction(mTransactionContext, mDb);
19451a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (mVisibleTouched) {
19461a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = false;
1947b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.updateAllVisible();
19481a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        }
19493826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
1950bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        updateSearchIndexInTransaction();
1951bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov
19523826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatusUpdateNeeded) {
19533826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
19543826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatusUpdateNeeded = false;
19553826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
1956b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1957b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1958bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov    private void updateSearchIndexInTransaction() {
1959bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        Set<Long> staleContacts = mTransactionContext.getStaleSearchIndexContactIds();
1960bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        Set<Long> staleRawContacts = mTransactionContext.getStaleSearchIndexRawContactIds();
1961bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        if (!staleContacts.isEmpty() || !staleRawContacts.isEmpty()) {
1962bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mSearchIndexManager.updateIndexForRawContacts(staleContacts, staleRawContacts);
1963bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mTransactionContext.clearSearchIndexUpdates();
1964bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        }
1965bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov    }
1966bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov
1967b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void flushTransactionalChanges() {
1968bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1969b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "flushTransactionChanges");
1970b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
19711129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
197224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // Determine whether we need to refresh the profile ID cache.
197324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        boolean profileCacheRefreshNeeded = false;
197424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1975d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        for (long rawContactId : mTransactionContext.getInsertedRawContactIds()) {
19768ab0b7a48efe540226253567bcf6fdbc487186a2Dmitri Plotnikov            mDbHelper.updateRawContactDisplayName(mDb, rawContactId);
1977bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mContactAggregator.onRawContactInsert(mTransactionContext, mDb, rawContactId);
1978285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        }
1979b5a4add17815167d20a90645779df34cdf45280dFred Quintana
198043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        Map<Long, AccountWithDataSet> insertedProfileRawContactAccountMap =
198124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                mTransactionContext.getInsertedProfileRawContactIds();
198224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (!insertedProfileRawContactAccountMap.isEmpty()) {
198324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            for (long profileRawContactId : insertedProfileRawContactAccountMap.keySet()) {
198424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                mDbHelper.updateRawContactDisplayName(mDb, profileRawContactId);
198524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                mContactAggregator.onProfileRawContactInsert(mTransactionContext, mDb,
198624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        profileRawContactId,
198724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        insertedProfileRawContactAccountMap.get(profileRawContactId));
198824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
198924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            profileCacheRefreshNeeded = true;
199024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
199124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1992d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        Set<Long> dirtyRawContacts = mTransactionContext.getDirtyRawContactIds();
1993d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!dirtyRawContacts.isEmpty()) {
1994a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
1995a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_DIRTY_SQL);
1996d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, dirtyRawContacts);
1997a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
1998a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
199924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
200024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            profileCacheRefreshNeeded = profileCacheRefreshNeeded ||
200124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    !Collections.disjoint(mProfileIdCache.profileRawContactIds, dirtyRawContacts);
2002a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        }
2003a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
2004d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        Set<Long> updatedRawContacts = mTransactionContext.getUpdatedRawContactIds();
2005d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!updatedRawContacts.isEmpty()) {
2006a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
2007a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_VERSION_SQL);
2008d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, updatedRawContacts);
2009a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
2010a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
201124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
201224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            profileCacheRefreshNeeded = profileCacheRefreshNeeded ||
201324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    !Collections.disjoint(mProfileIdCache.profileRawContactIds, updatedRawContacts);
2014b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2015b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2016d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        for (Map.Entry<Long, Object> entry : mTransactionContext.getUpdatedSyncStates()) {
2017b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long id = entry.getKey();
20189d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            if (mDbHelper.getSyncState().update(mDb, id, entry.getValue()) <= 0) {
20199d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                throw new IllegalStateException(
20209d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                        "unable to update sync state, does it still exist?");
20219d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            }
2022b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2023b5a4add17815167d20a90645779df34cdf45280dFred Quintana
202424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (profileCacheRefreshNeeded) {
202524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Force the profile ID cache to refresh.
202624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            mProfileIdCache.init(mDb, true);
202724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
202824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2029d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.clear();
2030b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2031b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2032a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /**
2033a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * Appends comma separated ids.
2034a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * @param ids Should not be empty
2035a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     */
2036d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov    private void appendIds(StringBuilder sb, Set<Long> ids) {
2037b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long id : ids) {
2038a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            sb.append(id).append(',');
2039b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2040a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
2041a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        sb.setLength(sb.length() - 1); // Yank the last comma
2042285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
2043285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
204424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
204524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Checks whether the given contact ID represents the user's personal profile - if it is, calls
204624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * a permission check (for writing the profile if forWrite is true, for reading the profile
204724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * otherwise).  If the contact ID is not the user's profile, no check is executed.
2048afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro     * @param db The database.
204924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param contactId The contact ID to be checked.
205024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forWrite Whether the caller is attempting to do a write (vs. read) operation.
205124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
2052afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro    private void enforceProfilePermissionForContact(SQLiteDatabase db, long contactId,
2053afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro            boolean forWrite) {
2054afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        mProfileIdCache.init(db, false);
205524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (mProfileIdCache.profileContactId == contactId) {
205624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermission(forWrite);
205724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
205824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
205924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
206024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
206124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Checks whether the given raw contact ID is a member of the user's personal profile - if it
206224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * is, calls a permission check (for writing the profile if forWrite is true, for reading the
206324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * profile otherwise).  If the raw contact ID is not in the user's profile, no check is
206424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * executed.
2065afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro     * @param db The database.
206624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param rawContactId The raw contact ID to be checked.
206724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forWrite Whether the caller is attempting to do a write (vs. read) operation.
206824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
2069afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro    private void enforceProfilePermissionForRawContact(SQLiteDatabase db, long rawContactId,
2070afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro            boolean forWrite) {
2071afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        mProfileIdCache.init(db, false);
207224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (mProfileIdCache.profileRawContactIds.contains(rawContactId)) {
207324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermission(forWrite);
207424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
207524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
207624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
207724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
207824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Checks whether the given data ID is a member of the user's personal profile - if it is,
207924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * calls a permission check (for writing the profile if forWrite is true, for reading the
208024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * profile otherwise).  If the data ID is not in the user's profile, no check is executed.
2081afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro     * @param db The database.
208224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param dataId The data ID to be checked.
208324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forWrite Whether the caller is attempting to do a write (vs. read) operation.
208424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
2085afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro    private void enforceProfilePermissionForData(SQLiteDatabase db, long dataId, boolean forWrite) {
2086afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        mProfileIdCache.init(db, false);
208724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (mProfileIdCache.profileDataIds.contains(dataId)) {
208824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermission(forWrite);
208924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
209024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
209124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
209224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
209324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Performs a permission check for WRITE_PROFILE or READ_PROFILE (depending on the parameter).
209424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * If the permission check fails, this will throw a SecurityException.
209524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forWrite Whether the caller is attempting to do a write (vs. read) operation.
209624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
209724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private void enforceProfilePermission(boolean forWrite) {
209824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        String profilePermission = forWrite
209924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                ? "android.permission.WRITE_PROFILE"
210024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                : "android.permission.READ_PROFILE";
210124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        getContext().enforceCallingOrSelfPermission(profilePermission, null);
210224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
210324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2104285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2105cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    protected void notifyChange() {
210681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        notifyChange(mSyncToNetwork);
210781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = false;
210881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
210981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
211081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    protected void notifyChange(boolean syncToNetwork) {
211181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
211281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                syncToNetwork);
2113cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    }
2114568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
211551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    protected void setProviderStatus(int status) {
21163826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != status) {
21173826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatus = status;
21183826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            getContext().getContentResolver().notifyChange(ProviderStatus.CONTENT_URI, null, false);
21193826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
212051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
212151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
2122f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    public DataRowHandler getDataRowHandler(final String mimeType) {
21233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
21243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
21256d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov            handler = new DataRowHandlerForCustomMimetype(
21266d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                    getContext(), mDbHelper, mContactAggregator, mimeType);
21273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
21283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
21293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
21303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
21313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
21324f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2133de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected Uri insertInTransaction(Uri uri, ContentValues values) {
2134bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
21351129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Log.v(TAG, "insertInTransaction: " + uri + " " + values);
2136b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2137f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2138f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2139f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2140f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2141a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
2142a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
214335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2144a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
214535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2146b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                id = mDbHelper.getSyncState().insert(mDb, values);
214735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
214835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2149d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
2150d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                insertContact(values);
21516bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
21526bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
21536bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
215424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
215524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                throw new UnsupportedOperationException(
215624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        "The profile contact is created automatically");
215724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
215824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
21595ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
216024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                id = insertRawContact(uri, values, callerIsSyncAdapter, false);
2161f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2162a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2163a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2164a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
21655ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
21665ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(1));
2167f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2168f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2169a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2170a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2171a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
21723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case RAW_CONTACTS_ID_STREAM_ITEMS: {
21733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                values.put(StreamItems.RAW_CONTACT_ID, uri.getPathSegments().get(1));
21743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItem(uri, values);
21753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
21763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
21773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
21783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
217924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS: {
218024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(true);
218124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                id = insertRawContact(uri, values, callerIsSyncAdapter, true);
218224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                mSyncToNetwork |= !callerIsSyncAdapter;
218324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
218424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
218524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2186a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case DATA: {
2187f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2188f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2189a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2190a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2191a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2192ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
2193f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                id = insertGroup(uri, values, callerIsSyncAdapter);
2194f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2195ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2196ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2197ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2198eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
21995aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertSettings(uri, values);
220043880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2201eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
2202eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2203eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
220482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
220582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                id = insertStatusUpdate(values);
22061f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
22071f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
22081f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
22093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
22103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItem(uri, values);
22113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
22123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
22133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
22143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
22153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
22163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItemPhoto(uri, values);
22173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
22183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
22193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
22203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
22213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
22223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                values.put(StreamItemPhotos.STREAM_ITEM_ID, uri.getPathSegments().get(1));
22233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItemPhoto(uri, values);
22243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
22253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
22263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
22273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
2228a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
222981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
2230f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
2231a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2232a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
22337e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
22347e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
22357e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
22367e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2237de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        return ContentUris.withAppendedId(uri, id);
2238a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2239a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2240a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2241e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * If account is non-null then store it in the values. If the account is
2242e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * already specified in the values then it must be consistent with the
2243e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * account, if it is non-null.
2244e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *
2245e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param uri Current {@link Uri} being operated on.
2246e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param values {@link ContentValues} to read and possibly update.
2247e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when only one of
2248e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_NAME} or
2249e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_TYPE} is specified, leaving the
2250e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             other undefined.
2251e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when {@link RawContacts#ACCOUNT_NAME}
2252e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             and {@link RawContacts#ACCOUNT_TYPE} are inconsistent between
2253e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             the given {@link Uri} and {@link ContentValues}.
22547e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
2255e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey    private Account resolveAccount(Uri uri, ContentValues values) throws IllegalArgumentException {
2256f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
2257f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
2258e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
2259f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2260f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
2261f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
2262e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialValues = TextUtils.isEmpty(valueAccountName)
2263e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                ^ TextUtils.isEmpty(valueAccountType);
2264e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2265e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri || partialValues) {
2266e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
2267fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
2268fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
2269e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
2270e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2271e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
2272e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
2273e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validUri = !TextUtils.isEmpty(accountName);
2274e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validValues = !TextUtils.isEmpty(valueAccountName);
2275e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2276e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validValues && validUri) {
2277e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Check that accounts match when both present
2278e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            final boolean accountMatch = TextUtils.equals(accountName, valueAccountName)
2279e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    && TextUtils.equals(accountType, valueAccountType);
2280e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            if (!accountMatch) {
2281fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new IllegalArgumentException(mDbHelper.exceptionMessage(
2282fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        "When both specified, ACCOUNT_NAME and ACCOUNT_TYPE must match", uri));
2283e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            }
2284e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validUri) {
2285e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Fill values from Uri when not present
2286f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_NAME, accountName);
2287f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_TYPE, accountType);
2288e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validValues) {
2289f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountName = valueAccountName;
2290f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountType = valueAccountType;
2291e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else {
2292e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            return null;
2293f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
2294f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2295e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Use cached Account object when matches, otherwise create
2296f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mAccount == null
2297f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.name.equals(accountName)
2298f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.type.equals(accountType)) {
2299f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mAccount = new Account(accountName, accountType);
2300035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
2301f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2302e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        return mAccount;
23037e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
23047e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
23057e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
230643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * Resolves the account and builds an {@link AccountWithDataSet} based on the data set specified
230743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * in the URI or values (if any).
230843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * @param uri Current {@link Uri} being operated on.
230943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * @param values {@link ContentValues} to read and possibly update.
231043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     */
231143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    private AccountWithDataSet resolveAccountWithDataSet(Uri uri, ContentValues values) {
23123593682b8d9213fde576a0cff54458ad50563980Dave Santoro        final Account account = resolveAccount(uri, values);
231343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        AccountWithDataSet accountWithDataSet = null;
231443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        if (account != null) {
231543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            String dataSet = getQueryParameter(uri, RawContacts.DATA_SET);
231643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            if (dataSet == null) {
23173593682b8d9213fde576a0cff54458ad50563980Dave Santoro                dataSet = values.getAsString(RawContacts.DATA_SET);
2318a71dc460ca951c7aca591f3f470c160cde70a1e3Dave Santoro            } else {
23193593682b8d9213fde576a0cff54458ad50563980Dave Santoro                values.put(RawContacts.DATA_SET, dataSet);
232043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
232143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            accountWithDataSet = new AccountWithDataSet(account.name, account.type, dataSet);
232243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        }
232343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        return accountWithDataSet;
232443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    }
232543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
232643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    /**
2327d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Inserts an item in the contacts table
23286bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
23296bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
23306bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
23316bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
2332d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertContact(ContentValues values) {
2333de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregate contacts are created automatically");
23346bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
23356bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
23366bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
233724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Inserts an item in the raw contacts table
2338a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2339f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param uri the values for the new row
2340f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param values the account this contact should be associated with. may be null.
2341dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana     * @param callerIsSyncAdapter
234224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forProfile Whether this raw contact is being inserted into the user's profile.
2343a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2344a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
234524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private long insertRawContact(Uri uri, ContentValues values, boolean callerIsSyncAdapter,
234624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            boolean forProfile) {
2347f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2348f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2349f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2350f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
235143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        AccountWithDataSet accountWithDataSet = resolveAccountWithDataSet(uri, mValues);
23527e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
23533d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
23543d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
2355f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
23563d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
23573d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2358f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long rawContactId = mDb.insert(Tables.RAW_CONTACTS, RawContacts.CONTACT_ID, mValues);
2359f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        int aggregationMode = RawContacts.AGGREGATION_MODE_DEFAULT;
236024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (forProfile) {
236124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Profile raw contacts should never be aggregated by the aggregator; they are always
236224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // aggregated under a single profile contact.
236324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            aggregationMode = RawContacts.AGGREGATION_MODE_DISABLED;
236424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        } else if (mValues.containsKey(RawContacts.AGGREGATION_MODE)) {
2365f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            aggregationMode = mValues.getAsInteger(RawContacts.AGGREGATION_MODE);
2366f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        }
2367f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        mContactAggregator.markNewForAggregation(rawContactId, aggregationMode);
2368285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
236924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (forProfile) {
237024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Trigger creation of the user profile Contact (or association with the existing one)
237124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // at the end of the transaction.
237243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            mTransactionContext.profileRawContactInserted(rawContactId, accountWithDataSet);
237324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        } else {
237424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Trigger creation of a Contact based on this RawContact at the end of transaction
237543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            mTransactionContext.rawContactInserted(rawContactId, accountWithDataSet);
237624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
2377f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2378dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter) {
2379dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            addAutoAddMembership(rawContactId);
2380dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            final Long starred = values.getAsLong(RawContacts.STARRED);
2381dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (starred != null && starred != 0) {
2382dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateFavoritesMembership(rawContactId, starred != 0);
2383dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2384dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2385dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
23863826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
2387023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        return rawContactId;
2388a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2389a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2390dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void addAutoAddMembership(long rawContactId) {
2391dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID,
2392dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2393dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2394dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            insertDataGroupMembership(rawContactId, groupId);
2395dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2396dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2397dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2398dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private Long findGroupByRawContactId(String selection, long rawContactId) {
2399dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        Cursor c = mDb.query(Tables.GROUPS + "," + Tables.RAW_CONTACTS, PROJECTION_GROUP_ID,
2400dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection,
2401dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                new String[]{Long.toString(rawContactId)},
2402dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                null /* groupBy */, null /* having */, null /* orderBy */);
2403dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        try {
2404dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            while (c.moveToNext()) {
2405dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return c.getLong(0);
2406dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2407dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return null;
2408dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        } finally {
2409dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            c.close();
2410dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2411dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2412dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2413dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void updateFavoritesMembership(long rawContactId, boolean isStarred) {
2414dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID,
2415dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2416dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2417dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (isStarred) {
2418dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                insertDataGroupMembership(rawContactId, groupId);
2419dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
2420dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                deleteDataGroupMembership(rawContactId, groupId);
2421dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2422dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2423dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2424dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2425dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void insertDataGroupMembership(long rawContactId, long groupId) {
2426dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        ContentValues groupMembershipValues = new ContentValues();
2427dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.GROUP_ROW_ID, groupId);
2428dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.RAW_CONTACT_ID, rawContactId);
2429dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(DataColumns.MIMETYPE_ID,
2430dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                mDbHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
2431dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mDb.insert(Tables.DATA, null, groupMembershipValues);
2432dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2433dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2434dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void deleteDataGroupMembership(long rawContactId, long groupId) {
2435dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final String[] selectionArgs = {
2436dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(mDbHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE)),
2437dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(groupId),
2438dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(rawContactId)};
2439dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mDb.delete(Tables.DATA, SELECTION_GROUPMEMBERSHIP_DATA, selectionArgs);
2440dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2441dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2442a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2443a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
2444a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2445a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
2446a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2447a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2448f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
2449a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
2450de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.clear();
2451de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.putAll(values);
245267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
2453de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
245420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
245524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // If the data being inserted belongs to the user's profile entry, check for the
245624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // WRITE_PROFILE permission before proceeding.
2457afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        enforceProfilePermissionForRawContact(mDb, rawContactId, true);
245824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2459de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace package with internal mapping
2460de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
2461de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (packageName != null) {
2462b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
2463de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
2464de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.RES_PACKAGE);
2465508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2466de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace mimetype with internal mapping
2467de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String mimeType = mValues.getAsString(Data.MIMETYPE);
2468de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (TextUtils.isEmpty(mimeType)) {
2469de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
2470de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
24714097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
2472b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.getMimeTypeId(mimeType));
2473de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
2474a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2475a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
2476d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        id = rowHandler.insert(mDb, mTransactionContext, rawContactId, mValues);
2477f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2478d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            mTransactionContext.markRawContactDirty(rawContactId);
2479a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2480d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.rawContactUpdated(rawContactId);
2481a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
24824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
24834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
24843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
24853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Inserts an item in the stream_items table.  The account is checked against the
24863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account in the raw contact for which the stream item is being inserted.  If the
24873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * new stream item results in more stream items under this raw contact than the limit,
24883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the oldest one will be deleted (note that if the stream item inserted was the
24893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * oldest, it will be immediately deleted, and this will return 0).
24903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
24913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param uri the insertion URI
24923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param values the values for the new row
24933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return the stream item _ID of the newly created row, or 0 if it was not created
24943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
24953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long insertStreamItem(Uri uri, ContentValues values) {
24963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long id = 0;
24973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.clear();
24983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.putAll(values);
24993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
25003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long rawContactId = mValues.getAsLong(StreamItems.RAW_CONTACT_ID);
25013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
25023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If the data being inserted belongs to the user's profile entry, check for the
25033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // WRITE_PROFILE permission before proceeding.
2504afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        enforceProfilePermissionForRawContact(mDb, rawContactId, true);
25053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
25063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Ensure that the raw contact exists and belongs to the caller's account.
25073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, mValues);
25083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccount(account, rawContactId);
25093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
25106802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to insert accounts params - they don't exist in the stream items table.
25116802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        mValues.remove(RawContacts.ACCOUNT_NAME);
25126802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        mValues.remove(RawContacts.ACCOUNT_TYPE);
25136802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
25143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Insert the new stream item.
25156802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        id = mDb.insert(Tables.STREAM_ITEMS, null, mValues);
25166802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (id == -1) {
25176802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Insertion failed.
25186802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return 0;
25196802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
25203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
25213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check to see if we're over the limit for stream items under this raw contact.
25223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // It's possible that the inserted stream item is older than the the existing
25233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // ones, in which case it may be deleted immediately (resetting the ID to 0).
25243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        id = cleanUpOldStreamItems(rawContactId, id);
25253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
25263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return id;
25273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
25283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
25293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
25303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Inserts an item in the stream_item_photos table.  The account is checked against
25313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the account in the raw contact that owns the stream item being modified.
25323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
25333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param uri the insertion URI
25343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param values the values for the new row
25356802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @return the stream item photo _ID of the newly created row, or 0 if there was an issue
25366802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     *     with processing the photo or creating the row
25373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
25383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long insertStreamItemPhoto(Uri uri, ContentValues values) {
25393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long id = 0;
25403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.clear();
25413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.putAll(values);
25423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
25433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long streamItemId = mValues.getAsLong(StreamItemPhotos.STREAM_ITEM_ID);
25443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        if (streamItemId != 0) {
25453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            long rawContactId = lookupRawContactIdForStreamId(streamItemId);
25463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
25473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            // If the data being inserted belongs to the user's profile entry, check for the
25483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            // WRITE_PROFILE permission before proceeding.
2549afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro            enforceProfilePermissionForRawContact(mDb, rawContactId, true);
25503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
25513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            // Ensure that the raw contact exists and belongs to the caller's account.
25523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            Account account = resolveAccount(uri, mValues);
25533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            enforceModifyingAccount(account, rawContactId);
25543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
25556802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Don't attempt to insert accounts params - they don't exist in the stream item
25566802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // photos table.
25576802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            mValues.remove(RawContacts.ACCOUNT_NAME);
25586802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            mValues.remove(RawContacts.ACCOUNT_TYPE);
25593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
25606802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Process the photo and store it.
25616802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            if (processStreamItemPhoto(mValues, false)) {
25626802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // Insert the stream item photo.
25636802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                id = mDb.insert(Tables.STREAM_ITEM_PHOTOS, null, mValues);
25646802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
25653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
25663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return id;
25673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
25683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
25693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
25706802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * Processes the photo contained in the {@link ContactsContract.StreamItemPhotos#PHOTO}
25716802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * field of the given values, attempting to store it in the photo store.  If successful,
25726802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * the resulting photo file ID will be added to the values for insert/update in the table.
25736802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * <p>
25746802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * If updating, it is valid for the picture to be empty or unspecified (the function will
25756802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * still return true).  If inserting, a valid picture must be specified.
25766802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @param values The content values provided by the caller.
25776802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @param forUpdate Whether this photo is being processed for update (vs. insert).
25786802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @return Whether the insert or update should proceed.
25796802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     */
25806802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    private boolean processStreamItemPhoto(ContentValues values, boolean forUpdate) {
25816802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (!values.containsKey(StreamItemPhotos.PHOTO)) {
25826802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return forUpdate;
25836802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
25846802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        byte[] photoBytes = values.getAsByteArray(StreamItemPhotos.PHOTO);
25856802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (photoBytes == null) {
25866802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return forUpdate;
25876802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
25886802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
25896802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Process the photo and store it.
25906802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        try {
25916802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            long photoFileId = mPhotoStore.insert(new PhotoProcessor(photoBytes,
25921dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                    mMaxDisplayPhotoDim, mMaxThumbnailPhotoDim, true), true);
25936802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            if (photoFileId != 0) {
25946802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                values.put(StreamItemPhotos.PHOTO_FILE_ID, photoFileId);
25956802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                values.remove(StreamItemPhotos.PHOTO);
25966802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                return true;
25976802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            } else {
25986802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // Couldn't store the photo, return 0.
25996802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                Log.e(TAG, "Could not process stream item photo for insert");
26006802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                return false;
26016802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
26026802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        } catch (IOException ioe) {
26036802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            Log.e(TAG, "Could not process stream item photo for insert", ioe);
26046802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return false;
26056802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
26066802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    }
26076802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
26086802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    /**
26093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Looks up the raw contact ID that owns the specified stream item.
26103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param streamItemId The ID of the stream item.
26113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The associated raw contact ID, or -1 if no such stream item exists.
26123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
26133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long lookupRawContactIdForStreamId(long streamItemId) {
26143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long rawContactId = -1;
26153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c = mDb.query(Tables.STREAM_ITEMS, new String[]{StreamItems.RAW_CONTACT_ID},
26163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                StreamItems._ID + "=?", new String[]{String.valueOf(streamItemId)},
26173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                null, null, null);
26183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
26193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if (c.moveToFirst()) {
26203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                rawContactId = c.getLong(0);
26213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
26223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
26233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
26243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
26253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return rawContactId;
26263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
26273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
26283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
26293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given raw contact ID is owned by the given account.
26303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * If the resolved account is null, this will return true iff the raw contact
26313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * is also associated with the "null" account.
26323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
26333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * If the resolved account does not match, this will throw a security exception.
26343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
26353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param rawContactId The raw contact ID to check for.
26363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
26373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void enforceModifyingAccount(Account account, long rawContactId) {
26383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        String accountSelection = RawContactsColumns.CONCRETE_ID + "=? AND "
26393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_NAME + "=? AND "
26403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + "=?";
26413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        String noAccountSelection = RawContactsColumns.CONCRETE_ID + "=? AND "
26423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " IS NULL AND "
26433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " IS NULL";
26443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c;
26453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        if (account != null) {
26463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContactsColumns.CONCRETE_ID},
26473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    accountSelection,
26483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    new String[]{String.valueOf(rawContactId), mAccount.name, mAccount.type},
26493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    null, null, null);
26503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } else {
26513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContactsColumns.CONCRETE_ID},
26523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    noAccountSelection, new String[]{String.valueOf(rawContactId)},
26533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    null, null, null);
26543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
26553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
26563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if(c.getCount() == 0) {
26573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                throw new SecurityException("Caller account does not match raw contact ID "
26583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    + rawContactId);
26593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
26603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
26613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
26623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
26633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
26643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
26653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
26663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given selection of stream items matches up with the given
26673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account.  If any of the raw contacts fail the account check, this will throw a
26683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * security exception.
26693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
26703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selection The selection.
26713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selectionArgs The selection arguments.
26723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The list of stream item IDs that would be included in this selection.
26733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
26743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private List<Long> enforceModifyingAccountForStreamItems(Account account, String selection,
26753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
26763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemIds = Lists.newArrayList();
26773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
26783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        setTablesAndProjectionMapForStreamItems(qb);
26793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c = qb.query(mDb,
26803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{StreamItems._ID, StreamItems.RAW_CONTACT_ID},
26813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selection, selectionArgs, null, null, null);
26823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
26833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            while (c.moveToNext()) {
26843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                streamItemIds.add(c.getLong(0));
26853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
26863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Throw a security exception if the account doesn't match the raw contact's.
26873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                enforceModifyingAccount(account, c.getLong(1));
26883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
26893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
26903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
26913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
26923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemIds;
26933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
26943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
26953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
26963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given selection of stream item photos matches up with the given
26973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account.  If any of the raw contacts fail the account check, this will throw a
26983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * security exception.
26993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
27003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selection The selection.
27013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selectionArgs The selection arguments.
27023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The list of stream item photo IDs that would be included in this selection.
27033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
27043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private List<Long> enforceModifyingAccountForStreamItemPhotos(Account account, String selection,
27053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
27063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemPhotoIds = Lists.newArrayList();
27073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
27083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        setTablesAndProjectionMapForStreamItemPhotos(qb);
27093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c = qb.query(mDb, new String[]{StreamItemPhotos._ID, StreamItems.RAW_CONTACT_ID},
27103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selection, selectionArgs, null, null, null);
27113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
27123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            while (c.moveToNext()) {
27133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                streamItemPhotoIds.add(c.getLong(0));
27143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Throw a security exception if the account doesn't match the raw contact's.
27163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                enforceModifyingAccount(account, c.getLong(1));
27173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
27183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
27193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
27203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
27213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemPhotoIds;
27223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
27233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
27253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Queries the database for stream items under the given raw contact.  If there are
27263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * more entries than {@link ContactsProvider2#MAX_STREAM_ITEMS_PER_RAW_CONTACT},
27273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the oldest entries (as determined by timestamp) will be deleted.
27283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param rawContactId The raw contact ID to examine for stream items.
27293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param insertedStreamItemId The ID of the stream item that was just inserted,
27303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     prompting this cleanup.  Callers may pass 0 if no insertion prompted the
27313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     cleanup.
27323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The ID of the inserted stream item if it still exists after cleanup;
27333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     0 otherwise.
27343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
27353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long cleanUpOldStreamItems(long rawContactId, long insertedStreamItemId) {
27363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long postCleanupInsertedStreamId = insertedStreamItemId;
27373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c = mDb.query(Tables.STREAM_ITEMS, new String[]{StreamItems._ID},
27383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                StreamItems.RAW_CONTACT_ID + "=?", new String[]{String.valueOf(rawContactId)},
27393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                null, null, StreamItems.TIMESTAMP + " DESC, " + StreamItems._ID + " DESC");
27403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
27413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            int streamItemCount = c.getCount();
27423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if (streamItemCount <= MAX_STREAM_ITEMS_PER_RAW_CONTACT) {
27433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Still under the limit - nothing to clean up!
27443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return insertedStreamItemId;
27453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            } else {
27463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                c.moveToLast();
27473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                while (c.getPosition() >= MAX_STREAM_ITEMS_PER_RAW_CONTACT) {
27483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    long streamItemId = c.getLong(0);
27493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    if (insertedStreamItemId == streamItemId) {
27503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        // The stream item just inserted is being deleted.
27513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        postCleanupInsertedStreamId = 0;
27523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    }
27533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    deleteStreamItem(c.getLong(0));
27543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    c.moveToPrevious();
27553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
27563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
27573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
27583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
27593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
27603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return postCleanupInsertedStreamId;
27613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
27623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
2763ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
27648ab0b7a48efe540226253567bcf6fdbc487186a2Dmitri Plotnikov        mDbHelper.updateRawContactDisplayName(db, rawContactId);
2765d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    }
2766d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
27679261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
276820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
276920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
2770f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
277120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
277220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2773de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
2774de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
2775f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataRowHandler.DataDeleteQuery.COLUMNS,
2776f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                selection, selectionArgs, null);
2777de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        try {
2778de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            while(c.moveToNext()) {
2779f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                long rawContactId = c.getLong(DataRowHandler.DataDeleteQuery.RAW_CONTACT_ID);
278024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
278124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // Check for write profile permission if the data belongs to the profile.
2782afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForRawContact(mDb, rawContactId, true);
278324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2784f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
2785a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                DataRowHandler rowHandler = getDataRowHandler(mimeType);
2786d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                count += rowHandler.delete(mDb, mTransactionContext, c);
2787f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                if (!callerIsSyncAdapter) {
2788d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                    mTransactionContext.markRawContactDirty(rawContactId);
278988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                }
279020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
279120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
2792de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            c.close();
279320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
279420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
279520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
279620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
279720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
279888e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    /**
279988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     * Delete a data row provided that it is one of the allowed mime types.
280088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     */
280120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
2802f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
280388e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
280488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
28054da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(dataId);
2806f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataRowHandler.DataDeleteQuery.COLUMNS, Data._ID + "=?",
28074da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1, null);
2808f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
280920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
281020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
281120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
281220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
281320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2814f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov            String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
281520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
281620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
281720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
281820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
281920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
282020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
282120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
282220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
282320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
28247a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException("Data type mismatch: expected "
282520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
282620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
282720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
282824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Check for write profile permission if the data belongs to the profile.
282924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            long rawContactId = c.getLong(DataRowHandler.DataDeleteQuery.RAW_CONTACT_ID);
2830afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro            enforceProfilePermissionForRawContact(mDb, rawContactId, true);
283124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2832a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            DataRowHandler rowHandler = getDataRowHandler(mimeType);
2833d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            return rowHandler.delete(mDb, mTransactionContext, c);
283420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
283520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
283620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
283720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
283820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
283920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
2840ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
2841ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
2842f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private long insertGroup(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
2843f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2844f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2845f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
28463593682b8d9213fde576a0cff54458ad50563980Dave Santoro        final AccountWithDataSet accountWithDataSet = resolveAccountWithDataSet(uri, mValues);
2847ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2848ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
2849f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String packageName = mValues.getAsString(Groups.RES_PACKAGE);
285067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
2851f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(GroupsColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
285267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
2853f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.remove(Groups.RES_PACKAGE);
2854ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2855dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final boolean isFavoritesGroup = mValues.getAsLong(Groups.FAVORITES) != null
2856dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                ? mValues.getAsLong(Groups.FAVORITES) != 0
2857dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                : false;
2858dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2859f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2860f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(Groups.DIRTY, 1);
286173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
286273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2863f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long result = mDb.insert(Tables.GROUPS, Groups.TITLE, mValues);
2864ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2865dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter && isFavoritesGroup) {
2866dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // add all starred raw contacts to this group
2867dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String selection;
2868dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs;
28693593682b8d9213fde576a0cff54458ad50563980Dave Santoro            if (accountWithDataSet == null) {
2870dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + " IS NULL AND "
287143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + RawContacts.ACCOUNT_TYPE + " IS NULL AND "
287243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + RawContacts.DATA_SET + " IS NULL";
2873dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = null;
28743593682b8d9213fde576a0cff54458ad50563980Dave Santoro            } else if (accountWithDataSet.getDataSet() == null) {
2875dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + "=? AND "
28763593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        + RawContacts.ACCOUNT_TYPE + "=? AND "
28773593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        + RawContacts.DATA_SET + " IS NULL";
28783593682b8d9213fde576a0cff54458ad50563980Dave Santoro                selectionArgs = new String[] {
28793593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getAccountName(),
28803593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getAccountType()
28813593682b8d9213fde576a0cff54458ad50563980Dave Santoro                };
288243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            } else {
288343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                selection = RawContacts.ACCOUNT_NAME + "=? AND "
288443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + RawContacts.ACCOUNT_TYPE + "=? AND "
288543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + RawContacts.DATA_SET + "=?";
28863593682b8d9213fde576a0cff54458ad50563980Dave Santoro                selectionArgs = new String[] {
28873593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getAccountName(),
28883593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getAccountType(),
28893593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getDataSet()
28903593682b8d9213fde576a0cff54458ad50563980Dave Santoro                };
2891dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2892dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            Cursor c = mDb.query(Tables.RAW_CONTACTS,
2893dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[]{RawContacts._ID, RawContacts.STARRED},
2894dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    selection, selectionArgs, null, null, null);
2895892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            try {
2896892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                while (c.moveToNext()) {
2897892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (c.getLong(1) != 0) {
2898892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        final long rawContactId = c.getLong(0);
2899892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        insertDataGroupMembership(rawContactId, result);
2900d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                        mTransactionContext.markRawContactDirty(rawContactId);
2901892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    }
2902dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
2903892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            } finally {
2904892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                c.close();
2905dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2906dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2907dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2908f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mValues.containsKey(Groups.GROUP_VISIBLE)) {
29091a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2910ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        }
2911ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2912ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        return result;
2913ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2914ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
29155aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private long insertSettings(Uri uri, ContentValues values) {
2916e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final long id = mDb.insert(Tables.SETTINGS, null, values);
29175aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
29181a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
29191a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2920e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
29211a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
2922e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return id;
2923e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2924e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2925ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
292682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov     * Inserts a status update.
29271f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
292882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    public long insertStatusUpdate(ContentValues values) {
292982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        final String handle = values.getAsString(StatusUpdates.IM_HANDLE);
29300a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        final Integer protocol = values.getAsInteger(StatusUpdates.PROTOCOL);
29314dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        String customProtocol = null;
29324dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
29330a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        if (protocol != null && protocol == Im.PROTOCOL_CUSTOM) {
293482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            customProtocol = values.getAsString(StatusUpdates.CUSTOM_PROTOCOL);
29354dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (TextUtils.isEmpty(customProtocol)) {
29364dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                throw new IllegalArgumentException(
29374dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                        "CUSTOM_PROTOCOL is required when PROTOCOL=PROTOCOL_CUSTOM");
29384dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
29391f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
29401f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2941dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long rawContactId = -1;
2942dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long contactId = -1;
294382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Long dataId = values.getAsLong(StatusUpdates.DATA_ID);
29446802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        String accountType = null;
29456802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        String accountName = null;
2946f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.setLength(0);
29472526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov        mSelectionArgs.clear();
2948dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        if (dataId != null) {
2949dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the contact info for the given data row.
2950dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
29512526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSb.append(Tables.DATA + "." + Data._ID + "=?");
29522526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSelectionArgs.add(String.valueOf(dataId));
29531f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
2954dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the data row to attach this presence update to
2955dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
29560a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(handle) || protocol == null) {
29570a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                throw new IllegalArgumentException("PROTOCOL and IM_HANDLE are required");
29580a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
29590a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2960dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // TODO: generalize to allow other providers to match against email
2961dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == protocol;
2962dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
29632a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov            String mimeTypeIdIm = String.valueOf(mDbHelper.getMimeTypeIdForIm());
2964dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            if (matchEmail) {
29652a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                String mimeTypeIdEmail = String.valueOf(mDbHelper.getMimeTypeIdForEmail());
2966f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
2967f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // The following hack forces SQLite to use the (mimetype_id,data1) index, otherwise
2968f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the "OR" conjunction confuses it and it switches to a full scan of
2969f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the raw_contacts table.
2970f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
2971f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // This code relies on the fact that Im.DATA and Email.DATA are in fact the same
2972f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // column - Data.DATA1
29732526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + " IN (?,?)" +
29742526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Data.DATA1 + "=?" +
29752526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND ((" + DataColumns.MIMETYPE_ID + "=? AND " + Im.PROTOCOL + "=?");
29762526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
29772526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
29782526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
29792526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
29802526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
2981dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
29822526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
29832526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
2984dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
29852526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(") OR (" + DataColumns.MIMETYPE_ID + "=?))");
29862526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
2987dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            } else {
29882526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + "=?" +
29892526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.PROTOCOL + "=?" +
29902526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.DATA + "=?");
29912526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
29922526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
29932526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
2994dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
29952526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
29962526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
2997dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
2998dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
29991f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
300082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.DATA_ID)) {
30012526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(" AND " + DataColumns.CONCRETE_ID + "=?");
30022526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(values.getAsString(StatusUpdates.DATA_ID));
3003dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
300470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
300570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
30061f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
30071f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
3008de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            cursor = mDb.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
30092526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.toString(), mSelectionArgs.toArray(EMPTY_STRING_ARRAY), null, null,
30104394086494fe7909aaca70f56fb4bb08beebf303Dmitri Plotnikov                    Clauses.CONTACT_VISIBLE + " DESC, " + Data.RAW_CONTACT_ID);
30111f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
301267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
30135ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
30146802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                accountType = cursor.getString(DataContactsQuery.ACCOUNT_TYPE);
30156802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                accountName = cursor.getString(DataContactsQuery.ACCOUNT_NAME);
3016e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
30171f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
30181f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
30191f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
30201f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
30211f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
302231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
302331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
302431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
30251f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
30261f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
302782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.PRESENCE)) {
3028a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (customProtocol == null) {
3029a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // We cannot allow a null in the custom protocol field, because SQLite3 does not
3030a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // properly enforce uniqueness of null values
3031a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                customProtocol = "";
3032a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
3033a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3034a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.clear();
303582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.DATA_ID, dataId);
3036a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.RAW_CONTACT_ID, rawContactId);
3037a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.CONTACT_ID, contactId);
303882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PROTOCOL, protocol);
303982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
304082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.IM_HANDLE, handle);
304182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.IM_ACCOUNT)) {
304282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                mValues.put(StatusUpdates.IM_ACCOUNT, values.getAsString(StatusUpdates.IM_ACCOUNT));
3043a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
304482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PRESENCE,
304582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    values.getAsString(StatusUpdates.PRESENCE));
3046aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori            mValues.put(StatusUpdates.CHAT_CAPABILITY,
3047aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                    values.getAsString(StatusUpdates.CHAT_CAPABILITY));
30481f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3049a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            // Insert the presence update
3050a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mDb.replace(Tables.PRESENCE, null, mValues);
3051a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3052e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
30530a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
305482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.STATUS)) {
305582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String status = values.getAsString(StatusUpdates.STATUS);
30560a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String resPackage = values.getAsString(StatusUpdates.STATUS_RES_PACKAGE);
30570bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            Resources resources = getContext().getResources();
30580bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            if (!TextUtils.isEmpty(resPackage)) {
30590bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                PackageManager pm = getContext().getPackageManager();
30600bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                try {
30610bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                    resources = pm.getResourcesForApplication(resPackage);
30620bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                } catch (NameNotFoundException e) {
30630bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                    Log.w(TAG, "Contact status update resource package not found: "
30640bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                            + resPackage);
30650bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                }
30660bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            }
30670bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            Integer labelResourceId = values.getAsInteger(StatusUpdates.STATUS_LABEL);
30680a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
30690bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            if ((labelResourceId == null || labelResourceId == 0) && protocol != null) {
30700bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                labelResourceId = Im.getProtocolLabelResource(protocol);
30710a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
30720bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            String labelResource = getResourceName(resources, "string", labelResourceId);
30730a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
30740bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            Integer iconResourceId = values.getAsInteger(StatusUpdates.STATUS_ICON);
30750a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            // TODO compute the default icon based on the protocol
30760a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
30770bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            String iconResource = getResourceName(resources, "drawable", iconResourceId);
30780bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann
3079a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (TextUtils.isEmpty(status)) {
308078fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                mDbHelper.deleteStatusUpdate(dataId);
3081a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            } else {
30826802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                Long timestamp = values.getAsLong(StatusUpdates.STATUS_TIMESTAMP);
30836802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (timestamp != null) {
30846802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    mDbHelper.replaceStatusUpdate(dataId, timestamp, status, resPackage,
30850bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                            iconResourceId, labelResourceId);
30866802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                } else {
30870bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                    mDbHelper.insertStatusUpdate(dataId, status, resPackage, iconResourceId,
30880bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                            labelResourceId);
30896802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                }
30906802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
30916802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // For forward compatibility with the new stream item API, insert this status update
30926802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // there as well.  If we already have a stream item from this source, update that
30936802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // one instead of inserting a new one (since the semantics of the old status update
30946802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // API is to only have a single record).
30956802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (rawContactId != -1 && !TextUtils.isEmpty(status)) {
30966802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    ContentValues streamItemValues = new ContentValues();
30976802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RAW_CONTACT_ID, rawContactId);
3098d5ef5903570e533a501abe6a8e3d533fdb5318fcFlavio Lerda                    // Status updates are text only but stream items are HTML.
3099e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda                    streamItemValues.put(StreamItems.TEXT, statusUpdateToHtml(status));
31006802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.COMMENTS, "");
31016802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_PACKAGE, resPackage);
31026802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_ICON, iconResource);
31036802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_LABEL, labelResource);
31046802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.TIMESTAMP,
31056802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            timestamp == null ? System.currentTimeMillis() : timestamp);
31066802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
31076802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // Note: The following is basically a workaround for the fact that status
31086802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // updates didn't do any sort of account enforcement, while social stream item
31096802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // updates do.  We can't expect callers of the old API to start passing account
31106802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // information along, so we just populate the account params appropriately for
311143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    // the raw contact.  Data set is not relevant here, as we only check account
311243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    // name and type.
31136802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    if (accountName != null && accountType != null) {
31146802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        streamItemValues.put(RawContacts.ACCOUNT_NAME, accountName);
31156802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        streamItemValues.put(RawContacts.ACCOUNT_TYPE, accountType);
31166802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    }
31176802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
31186802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // Check for an existing stream item from this source, and insert or update.
31196802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    Uri streamUri = StreamItems.CONTENT_URI;
31206802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    Cursor c = query(streamUri, new String[]{StreamItems._ID},
31216802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            StreamItems.RAW_CONTACT_ID + "=?",
31226802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            new String[]{String.valueOf(rawContactId)}, null);
31236802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    try {
31246802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        if (c.getCount() > 0) {
31256802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            c.moveToFirst();
31266802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            update(ContentUris.withAppendedId(streamUri, c.getLong(0)),
31276802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    streamItemValues, null, null);
31286802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        } else {
31296802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            insert(streamUri, streamItemValues);
31306802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        }
31316802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    } finally {
31326802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        c.close();
31336802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    }
31346802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                }
3135e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            }
3136e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        }
3137bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
3138a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (contactId != -1) {
3139f4015ab9ab7c26b766b5331fbf6655b8c54877eaDmitri Plotnikov            mContactAggregator.updateLastStatusUpdateId(contactId);
3140a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3141a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3142a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        return dataId;
31431f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
31441f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3145e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda    /** Converts a status update to HTML. */
3146e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda    private String statusUpdateToHtml(String status) {
3147e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda        String html = Html.toHtml(new SpannableString(status));
3148e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda        if (html.endsWith("\n")) {
3149e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda            html = html.substring(0, html.length() - 2);
3150e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda        }
3151e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda        return html;
3152e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda    }
3153e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda
31540bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann    private String getResourceName(Resources resources, String expectedType, Integer resourceId) {
31550bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann        try {
31560bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            if (resourceId == null || resourceId == 0) return null;
31570bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann
31580bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            // Resource has an invalid type (e.g. a string as icon)? ignore
31590bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            final String resourceEntryName = resources.getResourceEntryName(resourceId);
31600bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            final String resourceTypeName = resources.getResourceTypeName(resourceId);
31610bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            if (!expectedType.equals(resourceTypeName)) {
31620bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                Log.w(TAG, "Resource " + resourceId + " (" + resourceEntryName + ") is of type " +
31630bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                        resourceTypeName + " but " + expectedType + " is required.");
31640bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                return null;
31650bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            }
31660bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann
31670bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            return resourceEntryName;
31680bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann        } catch (NotFoundException e) {
31690bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            return null;
31700bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann        }
31710bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann    }
31720bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann
31734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3174de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
3175bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3176b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "deleteInTransaction: " + uri);
3177b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3178b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3179f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3180f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
3181508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
3182508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
318335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3184b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs);
318535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3186b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID:
3187b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3188b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3189b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
3190b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selectionWithId, selectionArgs);
3191b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3192cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            case CONTACTS: {
3193cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // TODO
3194cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return 0;
3195cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3196cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3197d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
3198d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3199dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
32006bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
32016bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
32029fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP: {
32032e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
32042e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
32052e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
3206fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
3207fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
32082e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
32092e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
32102e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
3211dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
32122e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
32132e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
32149fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP_ID: {
32159fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                // lookup contact by id and lookup key to see if they still match the actual record
32169fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final List<String> pathSegments = uri.getPathSegments();
32179fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final String lookupKey = pathSegments.get(2);
32189fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
32199fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                setTablesAndProjectionMapForContacts(lookupQb, uri, null);
3220a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
32219fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                String[] args;
32229fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                if (selectionArgs == null) {
32239fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[2];
32249fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } else {
32259fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[selectionArgs.length + 2];
32269fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
32279fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
32289fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                args[0] = String.valueOf(contactId);
322960de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann                args[1] = Uri.encode(lookupKey);
32309fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                lookupQb.appendWhere(Contacts._ID + "=? AND " + Contacts.LOOKUP_KEY + "=?");
32319fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final SQLiteDatabase db = mDbHelper.getReadableDatabase();
32329fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                Cursor c = query(db, lookupQb, null, selection, args, null, null, null);
32339fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                try {
32349fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    if (c.getCount() == 1) {
32359fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // contact was unmodified so go ahead and delete it
3236dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        return deleteContact(contactId, callerIsSyncAdapter);
32379fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    } else {
32389fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // row was changed (e.g. the merging might have changed), we got multiple
32399fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // rows or the supplied selection filtered the record out
32409fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        return 0;
32419fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    }
32429fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } finally {
32439fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    c.close();
32449fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
32459fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            }
32469fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
32472971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case RAW_CONTACTS: {
32482971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
3249fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                Cursor c = mDb.query(Tables.RAW_CONTACTS,
3250fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        new String[]{RawContacts._ID, RawContacts.CONTACT_ID},
3251e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
32522971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
32532971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
32542971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                        final long rawContactId = c.getLong(0);
3255fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        long contactId = c.getLong(1);
3256fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        numDeletes += deleteRawContact(rawContactId, contactId,
3257fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                                callerIsSyncAdapter);
32582971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
32592971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
32602971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
32612971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
32622971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
32632971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
32642971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
32655ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
32662971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                final long rawContactId = ContentUris.parseId(uri);
3267fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                return deleteRawContact(rawContactId, mDbHelper.getContactId(rawContactId),
3268fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        callerIsSyncAdapter);
3269508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3270508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
327120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
3272f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
3273944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                return deleteData(appendAccountToSelection(uri, selection), selectionArgs,
3274f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        callerIsSyncAdapter);
327520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
327620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
327748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
327848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
327948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
328048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
3281508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
3282f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
32834da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
32844da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                return deleteData(Data._ID + "=?", mSelectionArgs1, callerIsSyncAdapter);
3285ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3286ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3287ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3288f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
32895aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteGroup(uri, ContentUris.parseId(uri), callerIsSyncAdapter);
32902971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
32912971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
32922971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case GROUPS: {
32932971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
32942971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups._ID},
3295e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
32962971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
32972971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
32985aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        numDeletes += deleteGroup(uri, c.getLong(0), callerIsSyncAdapter);
32992971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
33002971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
33012971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
33022971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
330381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (numDeletes > 0) {
3304f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
330581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
33062971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
3307508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3308508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
3309eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
331043880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3311e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                return deleteSettings(uri, appendAccountToSelection(uri, selection), selectionArgs);
3312eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3313eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
331482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
33150a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                return deleteStatusUpdates(selection, selectionArgs);
33161f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
33171f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
33183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
33193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
33203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItems(uri, new ContentValues(), selection, selectionArgs);
33213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
33223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
33233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
33243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
33253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItems(uri, new ContentValues(),
33263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemsColumns.CONCRETE_ID + "=?",
33273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{uri.getLastPathSegment()});
33283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
33293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
33303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
33313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
33323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItemPhotos(uri, new ContentValues(), selection, selectionArgs);
33333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
33343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
33353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
33363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
33373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
33383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
33393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItemPhotos(uri, new ContentValues(),
33403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=? AND "
33413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                                + StreamItemPhotos.STREAM_ITEM_ID + "=?",
33423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{streamItemPhotoId, streamItemId});
33433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
33443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
334581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
334681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
33473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
334881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
3349508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
33504f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
33514f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
33521c8e40c18f92722b9bec6e8ce2e345a9828efa16Dmitri Plotnikov    public int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) {
3353ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
3354b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final long groupMembershipMimetypeId = mDbHelper
335594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
3356de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mDb.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
335794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
335894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupId, null);
335994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
336094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        try {
3361f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (callerIsSyncAdapter) {
3362de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
336394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            } else {
336494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.clear();
336594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.put(Groups.DELETED, 1);
3366f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mValues.put(Groups.DIRTY, 1);
3367de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId, null);
336894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            }
336994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        } finally {
33701a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
337194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
337294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
337394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
33745aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
3375e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.delete(Tables.SETTINGS, selection, selectionArgs);
33761a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        mVisibleTouched = true;
3377e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3378e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3379e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3380dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int deleteContact(long contactId, boolean callerIsSyncAdapter) {
3381afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        enforceProfilePermissionForContact(mDb, contactId, true);
338296b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(contactId);
3383cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
338496b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                RawContacts.CONTACT_ID + "=?", mSelectionArgs1,
338596b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                null, null, null);
3386cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        try {
3387cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            while (c.moveToNext()) {
3388cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                long rawContactId = c.getLong(0);
3389dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
3390cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3391cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } finally {
3392cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            c.close();
3393cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
3394cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
33953826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
33963826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
3397cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return mDb.delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
3398cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3399cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3400fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov    public int deleteRawContact(long rawContactId, long contactId, boolean callerIsSyncAdapter) {
3401afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        enforceProfilePermissionForRawContact(mDb, rawContactId, true);
34023389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
34033826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
34043826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
3405f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (callerIsSyncAdapter) {
340614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            mDb.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
3407fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            int count = mDb.delete(Tables.RAW_CONTACTS, RawContacts._ID + "=" + rawContactId, null);
3408fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            mContactAggregator.updateDisplayNameForContact(mDb, contactId);
3409fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return count;
341033b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } else {
3411b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.removeContactIfSingleton(rawContactId);
3412dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
341333b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        }
341433b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
341533b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
34160a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private int deleteStatusUpdates(String selection, String[] selectionArgs) {
34179705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // delete from both tables: presence and status_updates
34189705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // TODO should account type/name be appended to the where clause?
34199705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      if (VERBOSE_LOGGING) {
34209705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          Log.v(TAG, "deleting data from status_updates for " + selection);
34219705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      }
34229705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      mDb.delete(Tables.STATUS_UPDATES, getWhereClauseForStatusUpdatesTable(selection),
34239705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          selectionArgs);
34249705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      return mDb.delete(Tables.PRESENCE, selection, selectionArgs);
34250a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
34260a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
34273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItems(Uri uri, ContentValues values, String selection,
34283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
34293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // First query for the stream items to be deleted, and check that they belong
34303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // to the account.
34313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
34323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemIds = enforceModifyingAccountForStreamItems(
34333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                account, selection, selectionArgs);
34343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
34353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If no security exception has been thrown, we're fine to delete.
34363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        for (long streamItemId : streamItemIds) {
34373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            deleteStreamItem(streamItemId);
34383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
34393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
34403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mVisibleTouched = true;
34413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemIds.size();
34423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
34433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
34443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItem(long streamItemId) {
34453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Note that this does not enforce the modifying account.
34463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        deleteStreamItemPhotos(streamItemId);
34473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return mDb.delete(Tables.STREAM_ITEMS, StreamItems._ID + "=?",
34483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{String.valueOf(streamItemId)});
34493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
34503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
34513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItemPhotos(Uri uri, ContentValues values, String selection,
34523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
34533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // First query for the stream item photos to be deleted, and check that they
34543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // belong to the account.
34553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
34563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItemPhotos(account, selection, selectionArgs);
34573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
34583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If no security exception has been thrown, we're fine to delete.
34593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return mDb.delete(Tables.STREAM_ITEM_PHOTOS, selection, selectionArgs);
34603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
34613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
34623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItemPhotos(long streamItemId) {
34633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Note that this does not enforce the modifying account.
34643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return mDb.delete(Tables.STREAM_ITEM_PHOTOS, StreamItemPhotos.STREAM_ITEM_ID + "=?",
34653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{String.valueOf(streamItemId)});
34663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
34673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
3468dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int markRawContactAsDeleted(long rawContactId, boolean callerIsSyncAdapter) {
346981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = true;
347081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
3471cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.clear();
3472cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DELETED, 1);
3473cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
3474cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
3475cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
3476cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DIRTY, 1);
3477dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        return updateRawContact(rawContactId, mValues, callerIsSyncAdapter);
3478cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3479cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
34804f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3481de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
3482de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            String[] selectionArgs) {
3483bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3484b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "updateInTransaction: " + uri);
3485b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3486b5a4add17815167d20a90645779df34cdf45280dFred Quintana
348735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
348800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
348900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
3490b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (match == SYNCSTATE_ID && selection == null) {
3491b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long rowId = ContentUris.parseId(uri);
34921129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Object data = values.get(ContactsContract.SyncState.DATA);
3493d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            mTransactionContext.syncStateUpdated(rowId, data);
3494b5a4add17815167d20a90645779df34cdf45280dFred Quintana            return 1;
3495b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3496b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3497f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3498f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
349900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
350035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3501b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
3502b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        appendAccountToSelection(uri, selection), selectionArgs);
3503b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3504b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID: {
3505b5a4add17815167d20a90645779df34cdf45280dFred Quintana                selection = appendAccountToSelection(uri, selection);
3506b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3507b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3508b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
3509b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
3510b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        selectionWithId, selectionArgs);
3511b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
351235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3513d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
3514dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(values, selection, selectionArgs, callerIsSyncAdapter);
351500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
351600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
351700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3518d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
3519dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(ContentUris.parseId(uri), values, callerIsSyncAdapter);
3520c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
3521c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
3522c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
352324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
352424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // Restrict update to the user's profile.
352524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                StringBuilder profileSelection = new StringBuilder();
352624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                profileSelection.append(Contacts.IS_USER_PROFILE + "=1");
352724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                if (!TextUtils.isEmpty(selection)) {
352824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    profileSelection.append(" AND (").append(selection).append(")");
352924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                }
353024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                count = updateContactOptions(values, profileSelection.toString(), selectionArgs,
353124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        callerIsSyncAdapter);
353224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
353324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
353424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
35352e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
35362e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
35372e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
35382e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
35392e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
3540fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
3541fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
35422e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
35432e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
35442e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
3545dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(contactId, values, callerIsSyncAdapter);
35462e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                break;
35472e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
35482e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
35497d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            case RAW_CONTACTS_DATA: {
35507d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                final String rawContactId = uri.getPathSegments().get(1);
35517d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
35527d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                    + (selection == null ? "" : " AND " + selection);
35537d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
35547d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter);
35557d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
35567d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                break;
35577d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            }
35587d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
355920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
3560944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                count = updateData(uri, values, appendAccountToSelection(uri, selection),
3561f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
356281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3563f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
356481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
356520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
356620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3567c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
356848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
356948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
357048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
357148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
3572f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
357381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3574f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
357581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
357600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
357700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
35787e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
35795ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
35805ac5c70d1165309302ebcc931f51723e37d31e0bJeff Sharkey                selection = appendAccountToSelection(uri, selection);
3581dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateRawContacts(values, selection, selectionArgs, callerIsSyncAdapter);
35827e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
35837e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
35847e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
35855ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
358633b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
35874529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                if (selection != null) {
35884da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
35894da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=?"
3590dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                                    + " AND(" + selection + ")", selectionArgs,
3591dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
35924529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                } else {
35934da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(rawContactId);
3594dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    count = updateRawContacts(values, RawContacts._ID + "=?", mSelectionArgs1,
3595dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
35964529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                }
35977e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
35987e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
35997e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
3600cce1c9cf029f40b62955f4b545f94c993daefbd2Dave Santoro            case PROFILE_RAW_CONTACTS: {
3601cce1c9cf029f40b62955f4b545f94c993daefbd2Dave Santoro                // Restrict update to the user's profile.
3602cce1c9cf029f40b62955f4b545f94c993daefbd2Dave Santoro                StringBuilder profileSelection = new StringBuilder();
3603cce1c9cf029f40b62955f4b545f94c993daefbd2Dave Santoro                profileSelection.append(RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1");
3604cce1c9cf029f40b62955f4b545f94c993daefbd2Dave Santoro                if (!TextUtils.isEmpty(selection)) {
3605cce1c9cf029f40b62955f4b545f94c993daefbd2Dave Santoro                    profileSelection.append(" AND (").append(selection).append(")");
3606cce1c9cf029f40b62955f4b545f94c993daefbd2Dave Santoro                }
3607cce1c9cf029f40b62955f4b545f94c993daefbd2Dave Santoro                count = updateRawContacts(values, profileSelection.toString(), selectionArgs,
3608cce1c9cf029f40b62955f4b545f94c993daefbd2Dave Santoro                        callerIsSyncAdapter);
3609cce1c9cf029f40b62955f4b545f94c993daefbd2Dave Santoro                break;
3610cce1c9cf029f40b62955f4b545f94c993daefbd2Dave Santoro            }
3611cce1c9cf029f40b62955f4b545f94c993daefbd2Dave Santoro
3612ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
36135aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, appendAccountToSelection(uri, selection),
3614f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
361581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3616f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
361781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3618ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3619ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3620ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3621ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3622ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
36234da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(groupId));
36244da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                String selectionWithId = Groups._ID + "=? "
362573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                        + (selection == null ? "" : " AND " + selection);
36265aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, selectionWithId, selectionArgs,
36275aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        callerIsSyncAdapter);
362881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3629f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
363081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3631ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3632ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3633ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3634127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
3635de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                count = updateAggregationException(mDb, values);
3636b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
3637b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
3638b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
3639eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
3640e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                count = updateSettings(uri, values, appendAccountToSelection(uri, selection),
3641e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        selectionArgs);
364243880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3643eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
3644eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3645eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
36469705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            case STATUS_UPDATES: {
36479705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                count = updateStatusUpdate(uri, values, selection, selectionArgs);
36489705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                break;
36499705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            }
36509705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
36513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
36523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItems(uri, values, selection, selectionArgs);
36533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
36543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
36553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
36563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
36573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItems(uri, values, StreamItemsColumns.CONCRETE_ID + "=?",
36583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{uri.getLastPathSegment()});
36593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
36603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
36613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
36623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
36633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values, selection, selectionArgs);
36643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
36653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
36663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
36673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
36683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
36693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values,
36703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotos.STREAM_ITEM_ID + "=?", new String[]{streamItemId});
36713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
36723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
36733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
36743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
36753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
36763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
36773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values,
36783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=? AND " +
36793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                                StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=?",
36803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{streamItemPhotoId, streamItemId});
36813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
36823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
36833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
368472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            case DIRECTORIES: {
3685bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                mContactDirectoryManager.scanPackagesByUid(Binder.getCallingUid());
368672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                count = 1;
3687d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
3688d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
3689d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
369046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            case DATA_USAGE_FEEDBACK_ID: {
369146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (handleDataUsageFeedback(uri)) {
369246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    count = 1;
369346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                } else {
369446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    count = 0;
369546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
369646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                break;
369746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
369846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
369981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
370081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
3701f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
370281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
370300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
370400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
370500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
37064f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
37074f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
37089705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private int updateStatusUpdate(Uri uri, ContentValues values, String selection,
37099705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        String[] selectionArgs) {
37109705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // update status_updates table, if status is provided
37119705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO should account type/name be appended to the where clause?
37129705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        int updateCount = 0;
37139705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContentValues settableValues = getSettableColumnsForStatusUpdatesTable(values);
37149705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
37159705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.STATUS_UPDATES,
37169705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    settableValues,
37179705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    getWhereClauseForStatusUpdatesTable(selection),
37189705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selectionArgs);
37199705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
37209705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
37219705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // now update the Presence table
37229705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        settableValues = getSettableColumnsForPresenceTable(values);
37239705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
37249705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.PRESENCE, settableValues,
37259705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selection, selectionArgs);
37269705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
37279705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO updateCount is not entirely a valid count of updated rows because 2 tables could
37289705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // potentially get updated in this method.
37299705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return updateCount;
37309705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
37319705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
37323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int updateStreamItems(Uri uri, ContentValues values, String selection,
37333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
37343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Stream items can't be moved to a new raw contact.
37353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        values.remove(StreamItems.RAW_CONTACT_ID);
37363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
37373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check that the stream items being updated belong to the account.
37383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
37393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItems(account, selection, selectionArgs);
37403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
37416802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to update accounts params - they don't exist in the stream items table.
37426802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_NAME);
37436802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_TYPE);
37446802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
37453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If there's been no exception, the update should be fine.
37463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return mDb.update(Tables.STREAM_ITEMS, values, selection, selectionArgs);
37473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
37483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
37493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int updateStreamItemPhotos(Uri uri, ContentValues values, String selection,
37503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
37513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Stream item photos can't be moved to a new stream item.
37523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        values.remove(StreamItemPhotos.STREAM_ITEM_ID);
37533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
37543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check that the stream item photos being updated belong to the account.
37553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
37563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItemPhotos(account, selection, selectionArgs);
37573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
37586802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to update accounts params - they don't exist in the stream item
37596802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // photos table.
37606802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_NAME);
37616802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_TYPE);
37626802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
37636802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Process the photo (since we're updating, it's valid for the photo to not be present).
37646802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (processStreamItemPhoto(values, true)) {
37656802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // If there's been no exception, the update should be fine.
37666802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return mDb.update(Tables.STREAM_ITEM_PHOTOS, values, selection, selectionArgs);
37676802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
37686802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        return 0;
37693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
37703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
37719705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    /**
37729705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     * Build a where clause to select the rows to be updated in status_updates table.
37739705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     */
37749705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private String getWhereClauseForStatusUpdatesTable(String selection) {
37759705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.setLength(0);
37769705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE);
37779705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(selection);
37789705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(")");
37799705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mSb.toString();
37809705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
37819705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
37829705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForStatusUpdatesTable(ContentValues values) {
37839705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
37849705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS, values,
37859705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS);
37869705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_TIMESTAMP, values,
37879705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_TIMESTAMP);
37889705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_RES_PACKAGE, values,
37899705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_RES_PACKAGE);
37909705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_LABEL, values,
37919705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_LABEL);
37929705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_ICON, values,
37939705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_ICON);
37949705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
37959705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
37969705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
37979705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForPresenceTable(ContentValues values) {
37989705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
37999705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.PRESENCE, values,
38009705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.PRESENCE);
3801aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.CHAT_CAPABILITY, values,
3802aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                StatusUpdates.CHAT_CAPABILITY);
38039705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
38049705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
38059705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
38065aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int updateGroups(Uri uri, ContentValues values, String selectionWithId,
3807f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
380873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3809ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
3810ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
381173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        ContentValues updatedValues;
3812f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter && !values.containsKey(Groups.DIRTY)) {
381373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = mValues;
381473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.clear();
381573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.putAll(values);
381673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.put(Groups.DIRTY, 1);
381773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } else {
381873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = values;
381973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
382073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3821ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        int count = mDb.update(Tables.GROUPS, updatedValues, selectionWithId, selectionArgs);
38221a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) {
38231a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
382494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
382543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
382643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        // TODO: This will not work for groups that have a data set specified, since the content
382743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        // resolver will not be able to request a sync for the right source (unless it is updated
382843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        // to key off account with data set).
38296ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        if (updatedValues.containsKey(Groups.SHOULD_SYNC)
38301129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                && updatedValues.getAsInteger(Groups.SHOULD_SYNC) != 0) {
38316ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups.ACCOUNT_NAME,
3832e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    Groups.ACCOUNT_TYPE}, selectionWithId, selectionArgs, null,
38336ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    null, null);
38346ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountName;
38356ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountType;
38366ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            try {
38376ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                while (c.moveToNext()) {
38386ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountName = c.getString(0);
38396ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountType = c.getString(1);
384024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
38416ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        Account account = new Account(accountName, accountType);
3842ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        ContentResolver.requestSync(account, ContactsContract.AUTHORITY,
38436ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                                new Bundle());
38446ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        break;
38456ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    }
38466ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                }
38476ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            } finally {
38486ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                c.close();
38496ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            }
38506ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        }
385194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        return count;
385294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
385394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
3854b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private int updateSettings(Uri uri, ContentValues values, String selection,
3855b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            String[] selectionArgs) {
3856e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.update(Tables.SETTINGS, values, selection, selectionArgs);
38571a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
38581a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3859e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
3860e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3861e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3862e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3863dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContacts(ContentValues values, String selection, String[] selectionArgs,
3864dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
38654529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (values.containsKey(RawContacts.CONTACT_ID)) {
38664529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            throw new IllegalArgumentException(RawContacts.CONTACT_ID + " should not be included " +
38674529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    "in content values. Contact IDs are assigned automatically");
38684529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
386973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
387097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
387197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
387297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0");
387397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
387497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
38754529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = 0;
3876ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        Cursor cursor = mDb.query(Views.RAW_CONTACTS,
387751bf5ea9531b9da72caff607dbdf35fd6f61cbe2Jeff Sharkey                new String[] { RawContacts._ID }, selection,
38784529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                selectionArgs, null, null, null);
38794529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        try {
38804529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            while (cursor.moveToNext()) {
38814529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                long rawContactId = cursor.getLong(0);
3882dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContact(rawContactId, values, callerIsSyncAdapter);
38834529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count++;
38844529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            }
38854529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        } finally {
38864529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            cursor.close();
38874529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
38884529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
38894529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return count;
38904529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    }
38914529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
3892dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContact(long rawContactId, ContentValues values,
3893dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
389424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
389524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // Enforce profile permissions if the raw contact is in the user's profile.
3896afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        enforceProfilePermissionForRawContact(mDb, rawContactId, true);
389724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
389896b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        final String selection = RawContacts._ID + " = ?";
389996b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(rawContactId);
390019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final boolean requestUndoDelete = (values.containsKey(RawContacts.DELETED)
390119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                && values.getAsInteger(RawContacts.DELETED) == 0);
390219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int previousDeleted = 0;
3903ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType = null;
3904ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName = null;
390543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String dataSet = null;
390619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        if (requestUndoDelete) {
390719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            Cursor cursor = mDb.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS, selection,
390896b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                    mSelectionArgs1, null, null, null);
390919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            try {
391019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                if (cursor.moveToFirst()) {
391119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    previousDeleted = cursor.getInt(RawContactsQuery.DELETED);
3912ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountType = cursor.getString(RawContactsQuery.ACCOUNT_TYPE);
3913ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountName = cursor.getString(RawContactsQuery.ACCOUNT_NAME);
391443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    dataSet = cursor.getString(RawContactsQuery.DATA_SET);
391519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                }
391619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            } finally {
391719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                cursor.close();
391819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
391919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
392019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT);
392119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        }
3922f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
392396b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        int count = mDb.update(Tables.RAW_CONTACTS, values, selection, mSelectionArgs1);
39245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count != 0) {
3925f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            if (values.containsKey(RawContacts.AGGREGATION_MODE)) {
3926f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                int aggregationMode = values.getAsInteger(RawContacts.AGGREGATION_MODE);
3927f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov
3928f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // As per ContactsContract documentation, changing aggregation mode
3929f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // to DEFAULT should not trigger aggregation
3930f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                if (aggregationMode != RawContacts.AGGREGATION_MODE_DEFAULT) {
393169cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId, aggregationMode, false);
3932f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                }
3933f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            }
3934433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            if (values.containsKey(RawContacts.STARRED)) {
3935dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter) {
3936dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
3937dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            values.getAsLong(RawContacts.STARRED) != 0);
3938dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
39394529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                mContactAggregator.updateStarred(rawContactId);
3940dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
3941dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // if this raw contact is being associated with an account, then update the
3942dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // favorites group membership based on whether or not this contact is starred.
3943dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // If it is starred, add a group membership, if one doesn't already exist
3944dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // otherwise delete any matching group memberships.
3945dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
3946dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    boolean starred = 0 != DatabaseUtils.longForQuery(mDb,
3947dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            SELECTION_STARRED_FROM_RAW_CONTACTS,
3948dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            new String[]{Long.toString(rawContactId)});
3949dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId, starred);
3950dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
3951dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
3952dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3953dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // if this raw contact is being associated with an account, then add a
3954dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // group membership to the group marked as AutoAdd, if any.
3955dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
3956dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                addAutoAddMembership(rawContactId);
3957433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            }
3958dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3959285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (values.containsKey(RawContacts.SOURCE_ID)) {
39602b7a632bba423357ae5641f94da6a2f71afc523bDmitri Plotnikov                mContactAggregator.updateLookupKeyForRawContact(mDb, rawContactId);
3961285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
3962f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            if (values.containsKey(RawContacts.NAME_VERIFIED)) {
3963f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
3964f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // If setting NAME_VERIFIED for this raw contact, reset it for all
3965f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // other raw contacts in the same aggregate
3966f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                if (values.getAsInteger(RawContacts.NAME_VERIFIED) != 0) {
396778fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                    mDbHelper.resetNameVerifiedForOtherRawContacts(rawContactId);
3968f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                }
3969f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                mContactAggregator.updateDisplayNameForRawContact(mDb, rawContactId);
3970f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            }
397119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            if (requestUndoDelete && previousDeleted == 1) {
3972d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                mTransactionContext.rawContactInserted(rawContactId,
397343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        new AccountWithDataSet(accountName, accountType, dataSet));
397419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
39755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
39765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return count;
397733b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
397833b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
3979321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(Uri uri, ContentValues values, String selection,
3980f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
398120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
398220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
398320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
39845ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
398520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
398620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
398720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
398820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
398920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
3990b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
399120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
399220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
399397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
399497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
399597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    Data.IS_READ_ONLY + "=0");
399697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
399797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
3998653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        int count = 0;
399920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
4000653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
4001653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // so we don't need to worry about updating data we don't have permission to read.
4002f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // This query will be allowed to return profiles, and we'll do the permission check
4003f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // within the loop.
40046ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro        Cursor c = queryLocal(uri.buildUpon()
4005f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                .appendQueryParameter(ContactsContract.ALLOW_PROFILE, "1").build(),
4006f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                DataRowHandler.DataUpdateQuery.COLUMNS,
40076ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                selection, selectionArgs, null, -1 /* directory ID */,
40086ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                true /* suppress profile check */);
4009653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        try {
4010653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            while(c.moveToNext()) {
401124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // Check profile permission for the raw contact that owns each data record.
401224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = c.getLong(DataRowHandler.DataUpdateQuery.RAW_CONTACT_ID);
4013afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForRawContact(mDb, rawContactId, true);
401424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
4015f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count += updateData(mValues, c, callerIsSyncAdapter);
401620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
4017653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        } finally {
4018653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            c.close();
401920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
402020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
4021653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return count;
402220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
402320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
4024f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
4025653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        if (values.size() == 0) {
4026653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return 0;
4027321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
4028653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
4029f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        final String mimeType = c.getString(DataRowHandler.DataUpdateQuery.MIMETYPE);
4030a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
4031f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        boolean updated =
4032f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                rowHandler.update(mDb, mTransactionContext, values, c, callerIsSyncAdapter);
4033f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
4034f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
4035a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
4036f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        return updated ? 1 : 0;
4037321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    }
4038321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
40398c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(ContentValues values, String selection,
4040dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
40418c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        int count = 0;
4042ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        Cursor cursor = mDb.query(Views.CONTACTS,
404324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                new String[] { Contacts._ID, Contacts.IS_USER_PROFILE }, selection,
40448c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                selectionArgs, null, null, null);
40458c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        try {
40468c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            while (cursor.moveToNext()) {
40478c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                long contactId = cursor.getLong(0);
404824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
404924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // Check for profile write permission before updating a user's profile contact.
405024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                boolean isProfile = cursor.getInt(1) == 1;
405124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                if (isProfile) {
405224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    enforceProfilePermission(true);
405324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                }
405424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
4055dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateContactOptions(contactId, values, callerIsSyncAdapter);
40568c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count++;
40578c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            }
40588c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        } finally {
40598c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            cursor.close();
40608c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        }
40618c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
40628c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return count;
40638c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
40648c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
4065dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateContactOptions(long contactId, ContentValues values,
4066dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
4067d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
406824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // Check write permission if the contact is the user's profile.
4069afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro        enforceProfilePermissionForContact(mDb, contactId, true);
407024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
40718c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
4072b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
4073d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
4074b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
4075d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
4076b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
4077d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
4078b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
4079d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
4080b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
4081d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.STARRED);
4082d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
4083d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
40848c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.size() == 0) {
4085d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
4086d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
4087d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
40888c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.containsKey(RawContacts.STARRED)) {
4089c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            // Mark dirty when changing starred to trigger sync
40908c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            mValues.put(RawContacts.DIRTY, 1);
4091c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey        }
4092c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey
40934da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(contactId);
409497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        mDb.update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=?"
409597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                + " AND " + RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0", mSelectionArgs1);
40968c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
4097dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (mValues.containsKey(RawContacts.STARRED) && !callerIsSyncAdapter) {
4098ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann            Cursor cursor = mDb.query(Views.RAW_CONTACTS,
4099dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[] { RawContacts._ID }, RawContacts.CONTACT_ID + "=?",
4100dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    mSelectionArgs1, null, null, null);
4101dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            try {
4102dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                while (cursor.moveToNext()) {
4103dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    long rawContactId = cursor.getLong(0);
4104dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
4105dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            mValues.getAsLong(RawContacts.STARRED) != 0);
4106dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
4107dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } finally {
4108dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                cursor.close();
4109dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
4110dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
4111dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
41128c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // Copy changeable values to prevent automatically managed fields from
41138c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // being explicitly updated by clients.
41148c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
4115b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
41168c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
4117b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
41188c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
4119b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
41208c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
4121b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
41228c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
4123b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
41248c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.STARRED);
41258c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
41269b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        int rslt = mDb.update(Tables.CONTACTS, mValues, Contacts._ID + "=?", mSelectionArgs1);
41276e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori
41289b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        if (values.containsKey(Contacts.LAST_TIME_CONTACTED) &&
41299b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori                !values.containsKey(Contacts.TIMES_CONTACTED)) {
41309b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
41319b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
41329b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        }
41339b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        return rslt;
4134f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
4135d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
4136127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
4137127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
41380c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId1 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID1);
41390c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId2 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID2);
414080c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
4141ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId1;
4142ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId2;
41430c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (rcId1 < rcId2) {
41440c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId1;
41450c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId2;
41460c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
41470c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId1;
41480c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId2;
4149b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
4150127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
41510c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
41524da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[0] = String.valueOf(rawContactId1);
41534da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[1] = String.valueOf(rawContactId2);
41540c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.delete(Tables.AGGREGATION_EXCEPTIONS,
41554da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    AggregationExceptions.RAW_CONTACT_ID1 + "=? AND "
41564da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    + AggregationExceptions.RAW_CONTACT_ID2 + "=?", mSelectionArgs2);
41570c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
41586bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            ContentValues exceptionValues = new ContentValues(3);
41596bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
41600c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
41610c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
41620c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
41630c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    exceptionValues);
4164127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
4165127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
41663389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
416769cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId1,
416869cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
416969cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId2,
417069cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
4171dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov
4172bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        mContactAggregator.aggregateContact(mTransactionContext, db, rawContactId1);
4173bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        mContactAggregator.aggregateContact(mTransactionContext, db, rawContactId2);
4174127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
4175127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
4176127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
4177127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
4178b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
4179b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
418070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    public void onAccountsUpdated(Account[] accounts) {
4181bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
41823826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
41833826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
4184bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected boolean updateAccountsInBackground(Account[] accounts) {
4185f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao        // TODO : Check the unit test.
4186e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov        boolean accountsChanged = false;
418749d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov        mDb = mDbHelper.getWritableDatabase();
418870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        mDb.beginTransaction();
418970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        try {
419043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            Set<AccountWithDataSet> existingAccountsWithDataSets =
419143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    findValidAccountsWithDataSets(Tables.ACCOUNTS);
4192743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov
419343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // Add a row to the ACCOUNTS table (with no data set) for each new account.
4194743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            for (Account account : accounts) {
419543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                AccountWithDataSet accountWithDataSet = new AccountWithDataSet(
419643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        account.name, account.type, null);
419743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                if (!existingAccountsWithDataSets.contains(accountWithDataSet)) {
4198e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    accountsChanged = true;
419943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
420043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    // Add an account entry with an empty data set to match the account.
4201743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                    mDb.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME
420243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            + ", " + RawContacts.ACCOUNT_TYPE + ", " + RawContacts.DATA_SET
420343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            + ") VALUES (?, ?, ?)",
420443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            new String[] {
420543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getAccountName(),
420643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getAccountType(),
420743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getDataSet()
420843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            });
4209743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                }
4210743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            }
421148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
421243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // Check each of the existing sub-accounts against the account list.  If the owning
421343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // account no longer exists, the sub-account and all its data should be deleted.
421443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            List<AccountWithDataSet> accountsWithDataSetsToDelete =
421543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    new ArrayList<AccountWithDataSet>();
421643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            List<Account> accountList = Arrays.asList(accounts);
421743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            for (AccountWithDataSet accountWithDataSet : existingAccountsWithDataSets) {
421843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                Account owningAccount = new Account(
421943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        accountWithDataSet.getAccountName(), accountWithDataSet.getAccountType());
422043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                if (!accountList.contains(owningAccount)) {
422143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    accountsWithDataSetsToDelete.add(accountWithDataSet);
422243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                }
422370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
422470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
422543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            if (!accountsWithDataSetsToDelete.isEmpty()) {
4226e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                accountsChanged = true;
422743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                for (AccountWithDataSet accountWithDataSet : accountsWithDataSetsToDelete) {
422843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    Log.d(TAG, "removing data for removed account " + accountWithDataSet);
422943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    String[] accountParams = new String[] {
423043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            accountWithDataSet.getAccountName(),
423143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            accountWithDataSet.getAccountType()
423243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    };
423343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    String[] accountWithDataSetParams = accountWithDataSet.getDataSet() == null
423443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            ? accountParams
423543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            : new String[] {
423643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getAccountName(),
423743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getAccountType(),
423843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getDataSet()
423943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            };
424043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    String groupsDataSetClause = " AND " + Groups.DATA_SET
424143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            + (accountWithDataSet.getDataSet() == null ? " IS NULL" : " = ?");
424243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    String rawContactsDataSetClause = " AND " + RawContacts.DATA_SET
424343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            + (accountWithDataSet.getDataSet() == null ? " IS NULL" : " = ?");
424443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
4245e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4246e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.GROUPS +
4247e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Groups.ACCOUNT_NAME + " = ?" +
424843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    " AND " + Groups.ACCOUNT_TYPE + " = ?" +
424943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    groupsDataSetClause, accountWithDataSetParams);
4250e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4251e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.PRESENCE +
4252e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (" +
4253e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    "SELECT " + RawContacts._ID +
4254e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " FROM " + Tables.RAW_CONTACTS +
4255e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
425643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    " AND " + RawContacts.ACCOUNT_TYPE + " = ?" +
425743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    rawContactsDataSetClause + ")", accountWithDataSetParams);
4258e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4259e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.RAW_CONTACTS +
4260e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
426143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            " AND " + RawContacts.ACCOUNT_TYPE + " = ?" +
426243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            rawContactsDataSetClause, accountWithDataSetParams);
4263e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4264e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.SETTINGS +
4265e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Settings.ACCOUNT_NAME + " = ?" +
426643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            " AND " + Settings.ACCOUNT_TYPE + " = ?", accountParams);
4267e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4268e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.ACCOUNTS +
4269e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + "=?" +
427043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            " AND " + RawContacts.ACCOUNT_TYPE + "=?" +
427143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            rawContactsDataSetClause, accountWithDataSetParams);
4272d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    mDb.execSQL(
4273d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            "DELETE FROM " + Tables.DIRECTORIES +
4274d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            " WHERE " + Directory.ACCOUNT_NAME + "=?" +
427543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            " AND " + Directory.ACCOUNT_TYPE + "=?", accountParams);
42764458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    resetDirectoryCache();
4277e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                }
4278e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov
427933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // Find all aggregated contacts that used to contain the raw contacts
428033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // we have just deleted and see if they are still referencing the deleted
4281e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                // names or photos.  If so, fix up those contacts.
428233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                HashSet<Long> orphanContactIds = Sets.newHashSet();
428333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                Cursor cursor = mDb.rawQuery("SELECT " + Contacts._ID +
428433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " FROM " + Tables.CONTACTS +
428533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " WHERE (" + Contacts.NAME_RAW_CONTACT_ID + " NOT NULL AND " +
428669cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                Contacts.NAME_RAW_CONTACT_ID + " NOT IN " +
428769cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + RawContacts._ID +
428869cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.RAW_CONTACTS + "))" +
428933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " OR (" + Contacts.PHOTO_ID + " NOT NULL AND " +
429033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                                Contacts.PHOTO_ID + " NOT IN " +
429169cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + Data._ID +
429269cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.DATA + "))", null);
429333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                try {
429433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    while (cursor.moveToNext()) {
429533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        orphanContactIds.add(cursor.getLong(0));
429633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    }
429733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                } finally {
429833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    cursor.close();
429933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
430033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
430133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                for (Long contactId : orphanContactIds) {
4302bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov                    mContactAggregator.updateAggregateData(mTransactionContext, contactId);
430333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
4304e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                mDbHelper.updateAllVisible();
4305bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov                updateSearchIndexInTransaction();
430633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            }
430733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
430843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // Now that we've done the account-based additions and subtractions from the Accounts
430943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // table, check for raw contacts that have been added with a data set and add Accounts
431043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // entries for those if necessary.
431143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            existingAccountsWithDataSets = findValidAccountsWithDataSets(Tables.ACCOUNTS);
431243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            Set<AccountWithDataSet> rawContactAccountsWithDataSets =
431343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    findValidAccountsWithDataSets(Tables.RAW_CONTACTS);
431443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            rawContactAccountsWithDataSets.removeAll(existingAccountsWithDataSets);
431543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
431643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // Any remaining raw contact sub-accounts need to be added to the Accounts table.
431743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            for (AccountWithDataSet accountWithDataSet : rawContactAccountsWithDataSets) {
431843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                accountsChanged = true;
431943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
432043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                // Add an account entry to match the raw contact.
432143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                mDb.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME
432243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + ", " + RawContacts.ACCOUNT_TYPE + ", " + RawContacts.DATA_SET
432343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + ") VALUES (?, ?, ?)",
432443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        new String[] {
432543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                accountWithDataSet.getAccountName(),
432643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                accountWithDataSet.getAccountType(),
432743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                accountWithDataSet.getDataSet()
432843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        });
432943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
433043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
4331e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            if (accountsChanged) {
433243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                // TODO: Should sync state take data set into consideration?
4333e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                mDbHelper.getSyncState().onAccountsChanged(mDb, accounts);
4334e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            }
433570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.setTransactionSuccessful();
433670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } finally {
433770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.endTransaction();
433870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
433973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.clear();
43403826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
43413826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (accountsChanged) {
43423826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateContactsAccountCount(accounts);
43433826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
43443826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
43453826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
4346afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov        return accountsChanged;
434770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
4348619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
43493826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateContactsAccountCount(Account[] accounts) {
43503826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        int count = 0;
43513826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        for (Account account : accounts) {
43523826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            if (isContactsAccount(account)) {
43533826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                count++;
43543826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            }
43553826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
43563826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mContactsAccountCount = count;
43573826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
43583826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
43593826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    protected boolean isContactsAccount(Account account) {
43603826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        final IContentService cs = ContentResolver.getContentService();
43613826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        try {
43623826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return cs.getIsSyncable(account, ContactsContract.AUTHORITY) > 0;
43633826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } catch (RemoteException e) {
43643826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            Log.e(TAG, "Cannot obtain sync flag for account: " + account, e);
43653826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return false;
43663826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
43673826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
43683826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
436972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void onPackageChanged(String packageName) {
4370bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_DIRECTORIES, packageName);
4371d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4372d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4373619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
437443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * Finds all distinct account types and data sets present in the specified table.
4375627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     */
437643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    private Set<AccountWithDataSet> findValidAccountsWithDataSets(String table) {
437743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        Set<AccountWithDataSet> accountsWithDataSets = new HashSet<AccountWithDataSet>();
4378743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov        Cursor c = mDb.rawQuery(
437943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                "SELECT DISTINCT " + RawContacts.ACCOUNT_NAME + "," + RawContacts.ACCOUNT_TYPE +
438043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                "," + RawContacts.DATA_SET +
438143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                " FROM " + table, null);
4382627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
4383627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            while (c.moveToNext()) {
4384dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!c.isNull(0) || !c.isNull(1)) {
438543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    accountsWithDataSets.add(
438643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            new AccountWithDataSet(c.getString(0), c.getString(1), c.getString(2)));
4387627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
4388627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
4389627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } finally {
4390627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            c.close();
4391627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
439243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        return accountsWithDataSets;
4393627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
4394627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
43954f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
43964f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
43974f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
439815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
439915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mReadAccessLatch);
440015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
4401d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String directory = getQueryParameter(uri, ContactsContract.DIRECTORY_PARAM_KEY);
4402385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directory == null) {
44033716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            return wrapCursor(uri,
44046ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder, -1, false));
4405385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directory.equals("0")) {
44063716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            return wrapCursor(uri,
44073716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder,
44086ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                            Directory.DEFAULT, false));
4409d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        } else if (directory.equals("1")) {
44103716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            return wrapCursor(uri,
44113716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder,
44126ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                            Directory.LOCAL_INVISIBLE, false));
4413d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4414d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4415d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        DirectoryInfo directoryInfo = getDirectoryAuthority(directory);
4416d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo == null) {
4417a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            Log.e(TAG, "Invalid directory ID: " + uri);
4418a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            return null;
4419d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4420d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4421d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Builder builder = new Uri.Builder();
4422d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.scheme(ContentResolver.SCHEME_CONTENT);
4423d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.authority(directoryInfo.authority);
4424d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.encodedPath(uri.getEncodedPath());
4425d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountName != null) {
4426d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_NAME, directoryInfo.accountName);
4427d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4428d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountType != null) {
4429d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, directoryInfo.accountType);
4430d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
44312e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
44322e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limit = getLimit(uri);
44332e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        if (limit != null) {
44342e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov            builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, limit);
44352e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        }
44362e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
4437d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Uri directoryUri = builder.build();
443809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
443909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        if (projection == null) {
444009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            projection = getDefaultProjection(uri);
444109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
444209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
4443332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        Cursor cursor = getContext().getContentResolver().query(directoryUri, projection, selection,
4444d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                selectionArgs, sortOrder);
44456ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
44466ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (cursor == null) {
44476ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return null;
44486ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
44496ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
4450547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        CrossProcessCursor crossProcessCursor = getCrossProcessCursor(cursor);
4451547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        if (crossProcessCursor != null) {
4452547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro            return wrapCursor(uri, cursor);
4453547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        } else {
4454547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro            return matrixCursorFromCursor(wrapCursor(uri, cursor));
4455547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        }
44563716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    }
44573716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
4458547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro    private Cursor wrapCursor(Uri uri, Cursor cursor) {
4459547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro
4460547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        // If the cursor doesn't contain a snippet column, don't bother wrapping it.
4461547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        if (cursor.getColumnIndex(SearchSnippetColumns.SNIPPET) < 0) {
44625517770250b3afa4fd88b6869c3244680821d222Dave Santoro            if (VERBOSE_LOGGING) {
44635517770250b3afa4fd88b6869c3244680821d222Dave Santoro                return new InstrumentedCursorWrapper(cursor, uri, TAG);
44645517770250b3afa4fd88b6869c3244680821d222Dave Santoro            } else {
44655517770250b3afa4fd88b6869c3244680821d222Dave Santoro                return cursor;
44665517770250b3afa4fd88b6869c3244680821d222Dave Santoro            }
4467547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        }
4468547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro
44693716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // Parse out snippet arguments for use when snippets are retrieved from the cursor.
44703716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String[] args = null;
44713716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String snippetArgs =
44723716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                getQueryParameter(uri, SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY);
44733716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        if (snippetArgs != null) {
44743716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            args = snippetArgs.split(",");
44753716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        }
44763716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
44773716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String query = uri.getLastPathSegment();
44783716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String startMatch = args != null && args.length > 0 ? args[0]
44793716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_START_MATCH;
44803716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String endMatch = args != null && args.length > 1 ? args[1]
44813716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_END_MATCH;
44823716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String ellipsis = args != null && args.length > 2 ? args[2]
44833716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_ELLIPSIS;
44843716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        int maxTokens = args != null && args.length > 3 ? Integer.parseInt(args[3])
44853716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_MAX_TOKENS;
44863716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
44875517770250b3afa4fd88b6869c3244680821d222Dave Santoro        if (VERBOSE_LOGGING) {
44885517770250b3afa4fd88b6869c3244680821d222Dave Santoro            return new InstrumentedCursorWrapper(new SnippetizingCursorWrapper(
44895517770250b3afa4fd88b6869c3244680821d222Dave Santoro                    cursor, query, startMatch, endMatch, ellipsis, maxTokens), uri, TAG);
44905517770250b3afa4fd88b6869c3244680821d222Dave Santoro        } else {
44915517770250b3afa4fd88b6869c3244680821d222Dave Santoro            return new SnippetizingCursorWrapper(cursor, query, startMatch, endMatch, ellipsis,
44925517770250b3afa4fd88b6869c3244680821d222Dave Santoro                    maxTokens);
44935517770250b3afa4fd88b6869c3244680821d222Dave Santoro        }
44946ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
44956ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
44966ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    private CrossProcessCursor getCrossProcessCursor(Cursor cursor) {
44976ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        Cursor c = cursor;
44986ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (c instanceof CrossProcessCursor) {
44996ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return (CrossProcessCursor) c;
45006ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else if (c instanceof CursorWindow) {
45016ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return getCrossProcessCursor(((CursorWrapper) c).getWrappedCursor());
45026ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else {
45036ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return null;
45046ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
45056ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
45066ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
45076ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    public MatrixCursor matrixCursorFromCursor(Cursor cursor) {
45086ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        MatrixCursor newCursor = new MatrixCursor(cursor.getColumnNames());
45096ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        int numColumns = cursor.getColumnCount();
45106ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        String data[] = new String[numColumns];
45116ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        cursor.moveToPosition(-1);
45126ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        while (cursor.moveToNext()) {
45136ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            for (int i = 0; i < numColumns; i++) {
45146ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov                data[i] = cursor.getString(i);
45156ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            }
45166ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            newCursor.addRow(data);
4517332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        }
45186ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        return newCursor;
4519d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4520d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4521d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final class DirectoryQuery {
4522d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
4523d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory._ID,
4524d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.DIRECTORY_AUTHORITY,
4525d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_NAME,
4526d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_TYPE
4527d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        };
4528d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4529d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int DIRECTORY_ID = 0;
4530d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int AUTHORITY = 1;
4531d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_NAME = 2;
4532d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_TYPE = 3;
4533d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4534d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4535d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
4536d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Reads and caches directory information for the database.
4537d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
4538d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private DirectoryInfo getDirectoryAuthority(String directoryId) {
45394458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized (mDirectoryCache) {
45404458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            if (!mDirectoryCacheValid) {
45414458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCache.clear();
454249d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                SQLiteDatabase db = mDbHelper.getReadableDatabase();
454349d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                Cursor cursor = db.query(Tables.DIRECTORIES,
45444458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryQuery.COLUMNS,
45454458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        null, null, null, null, null);
45464458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                try {
45474458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    while (cursor.moveToNext()) {
45484458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryInfo info = new DirectoryInfo();
45494458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        String id = cursor.getString(DirectoryQuery.DIRECTORY_ID);
45504458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.authority = cursor.getString(DirectoryQuery.AUTHORITY);
45514458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
45524458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
45534458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        mDirectoryCache.put(id, info);
45544458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    }
45554458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                } finally {
45564458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    cursor.close();
4557d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                }
45584458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCacheValid = true;
4559d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4560d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
45614458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            return mDirectoryCache.get(directoryId);
45624458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
4563d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4564d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
456572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void resetDirectoryCache() {
45664458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized(mDirectoryCache) {
45674458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            mDirectoryCacheValid = false;
45684458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
456972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
457072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
45716ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro    private Cursor queryLocal(Uri uri, String[] projection, String selection,
45726ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro            String[] selectionArgs, String sortOrder, long directoryId,
45734b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki            final boolean suppressProfileCheck) {
4574bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
4575bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            Log.v(TAG, "query: " + uri);
4576bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        }
45770b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov
4578b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
457935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4580d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
45811f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
4582c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limit = getLimit(uri);
4583c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
45844b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki        // Column name for appendProfileRestriction().  We append the profile check to the original
45854b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki        // selection if it's not null.
45864b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki        String profileRestrictionColumnName = null;
45874b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki
4588a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
45894f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
459035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
4591b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().query(db, projection, selection,  selectionArgs,
459235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                        sortOrder);
459335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4594d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
4595763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
45964b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                appendLocalDirectorySelectionIfNeeded(qb, directoryId);
45974b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                profileRestrictionColumnName = Contacts.IS_USER_PROFILE;
45986ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                sortOrder = prependProfileSortIfNeeded(uri, sortOrder, suppressProfileCheck);
4599619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
4600619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
4601619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
4602d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
46034a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
4604afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
4605763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
46064da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
46074da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
46086bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
46096bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
46106bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
46115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP:
46125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
46135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
46145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int segmentCount = pathSegments.size();
46155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount < 3) {
4616fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
4617fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
46185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
4619a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
46205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String lookupKey = pathSegments.get(2);
46215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount == 4) {
46225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
4623afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                    enforceProfilePermissionForContact(db, contactId, false);
46245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
4625763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
4626a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4627a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
4628a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4629a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
4630a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
46315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        return c;
46325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
46335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
46345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
4635763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
46364da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
46374da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
46384da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
46395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                break;
46405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
46415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
46422149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_DATA:
46432149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_DATA: {
46442149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
46452149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                int segmentCount = pathSegments.size();
46462149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount < 4) {
46472149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
46482149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                            "Missing a lookup key", uri));
46492149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
46502149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
46512149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount == 5) {
46522149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
4653afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                    enforceProfilePermissionForContact(db, contactId, false);
46542149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
46552149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    setTablesAndProjectionMapForData(lookupQb, uri, projection, false);
4656a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
4657a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
4658a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4659a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Data.CONTACT_ID, contactId, Data.LOOKUP_KEY, lookupKey);
4660a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
46612149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                        return c;
46622149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    }
46632149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
46642149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    // TODO see if the contact exists but has no data rows (rare)
46652149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
46662149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
46672149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
466824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long contactId = lookupContactIdByLookupKey(db, lookupKey);
4669afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
46702149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
467124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        String.valueOf(contactId));
46722149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                qb.appendWhere(" AND " + Data.CONTACT_ID + "=?");
46732149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                break;
46742149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            }
46752149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
46763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_ID_STREAM_ITEMS: {
46773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                long contactId = Long.parseLong(uri.getPathSegments().get(1));
4678afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
46793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
46803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
46813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(RawContactsColumns.CONCRETE_CONTACT_ID + "=?");
46823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
46833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
46843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
46853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_LOOKUP_STREAM_ITEMS:
46863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_LOOKUP_ID_STREAM_ITEMS: {
46873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                List<String> pathSegments = uri.getPathSegments();
46883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                int segmentCount = pathSegments.size();
46893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                if (segmentCount < 4) {
46903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
46913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                            "Missing a lookup key", uri));
46923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
46933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String lookupKey = pathSegments.get(2);
46943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                if (segmentCount == 5) {
46953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    long contactId = Long.parseLong(pathSegments.get(3));
4696afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                    enforceProfilePermissionForContact(db, contactId, false);
46973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
46983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    setTablesAndProjectionMapForStreamItems(lookupQb);
46993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
47003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
47013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                            RawContacts.CONTACT_ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
47023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    if (c != null) {
47033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        return c;
47043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    }
47053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
47063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
47073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
47083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                long contactId = lookupContactIdByLookupKey(db, lookupKey);
4709afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
47103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
47113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(RawContacts.CONTACT_ID + "=?");
47123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
47133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
47143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
4715f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
471642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
471724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long contactId = lookupContactIdByLookupKey(db, lookupKey);
4718afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
4719ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
4720f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                qb.setProjectionMap(sContactsVCardProjectionMap);
47214da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
472224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        String.valueOf(contactId));
47234da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
4724f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                break;
4725f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            }
4726f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
472742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
472842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
472942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                String currentDateString = dateFormat.format(new Date()).toString();
473042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                return db.rawQuery(
473142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    "SELECT" +
473242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " 'vcards_' || ? || '.vcf' AS " + OpenableColumns.DISPLAY_NAME + "," +
473342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " NULL AS " + OpenableColumns.SIZE,
473442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    new String[] { currentDateString });
473542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
473642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
4737ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_FILTER: {
4738916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                String filterParam = "";
4739ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
4740916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    filterParam = uri.getLastPathSegment();
4741ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
47427ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov                setTablesAndProjectionMapForContactsWithSnippet(
47437ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov                        qb, uri, projection, filterParam, directoryId);
47444b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                profileRestrictionColumnName = Contacts.IS_USER_PROFILE;
47456ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro                sortOrder = prependProfileSortIfNeeded(uri, sortOrder, suppressProfileCheck);
4746ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4747ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4748ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
4749ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT_FILTER:
4750ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT: {
47512f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Basically the resultant SQL should look like this:
47522f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // (SQL for listing starred items)
47532f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // UNION ALL
47542f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // (SQL for listing frequently contacted items)
47552f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // ORDER BY ...
47562f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
47572f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final boolean phoneOnly = readBooleanQueryParameter(
47582f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        uri, ContactsContract.STREQUENT_PHONE_ONLY, false);
47592f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (match == CONTACTS_STREQUENT_FILTER && uri.getPathSegments().size() > 3) {
47604a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
47614a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
4762e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
47635e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
47642f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    selection = DbQueryUtils.concatenateClauses(selection, sb.toString());
47654a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
47664a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
47672f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                String[] subProjection = null;
47685e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                if (projection != null) {
47692f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    subProjection = appendProjectionArg(projection, TIMES_USED_SORT_COLUMN);
47705e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                }
47715e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
47724a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                // Build the first query for starred
47734928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                setTablesAndProjectionMapForContacts(qb, uri, projection, false);
47744928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                qb.setProjectionMap(phoneOnly ?
47754928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                        sStrequentPhoneOnlyStarredProjectionMap
47764928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                        : sStrequentStarredProjectionMap);
47772f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                qb.appendWhere(DbQueryUtils.concatenateClauses(
47782f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        selection, Contacts.IS_USER_PROFILE + "=0"));
47799dbfd650ccf93714f3266e80f9fbdbcb526ae7b3Daisuke Miyakawa                if (phoneOnly) {
47809dbfd650ccf93714f3266e80f9fbdbcb526ae7b3Daisuke Miyakawa                    qb.appendWhere(" AND " + Contacts.HAS_PHONE_NUMBER + "=1");
47819dbfd650ccf93714f3266e80f9fbdbcb526ae7b3Daisuke Miyakawa                }
47822f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                qb.setStrict(true);
47832f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final String starredQuery = qb.buildQuery(subProjection,
478424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        Contacts.STARRED + "=1", Contacts._ID, null, null, null);
4785d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
47862f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Reset the builder.
4787d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
47882f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                qb.setStrict(true);
47894928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
47904928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                // Build the second query for frequent part.
47914928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                final String frequentQuery;
47924928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                if (phoneOnly) {
47934928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    final StringBuilder tableBuilder = new StringBuilder();
47944928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // In phone only mode, we need to look at view_data instead of
47954928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // contacts/raw_contacts to obtain actual phone numbers. One problem is that
47964928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // view_data is much larger than view_contacts, so our query might become much
47974928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // slower.
47984928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    //
47994928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // To avoid the possible slow down, we start from data usage table and join
48004928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // view_data to the table, assuming data usage table is quite smaller than
48014928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // data rows (almost always it should be), and we don't want any phone
48024928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // numbers not used by the user. This way sqlite is able to drop a number of
48034928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // rows in view_data in the early stage of data lookup.
48044928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    tableBuilder.append(Tables.DATA_USAGE_STAT
48054928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + " INNER JOIN " + Views.DATA + " " + Tables.DATA
48064928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "="
48074928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                                + DataColumns.CONCRETE_ID + " AND "
48084928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "="
48094928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                                + DataUsageStatColumns.USAGE_TYPE_INT_CALL + ")");
48104928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    appendContactPresenceJoin(tableBuilder, projection, RawContacts.CONTACT_ID);
48114928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    appendContactStatusUpdateJoin(tableBuilder, projection,
48124928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            ContactsColumns.LAST_STATUS_UPDATE_ID);
48134928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
48144928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setTables(tableBuilder.toString());
48154928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setProjectionMap(sStrequentPhoneOnlyFrequentProjectionMap);
48164928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.appendWhere(DbQueryUtils.concatenateClauses(
48174928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            selection,
48184928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            Contacts.STARRED + "=0 OR " + Contacts.STARRED + " IS NULL",
48194928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            MimetypesColumns.MIMETYPE + " IN ("
48204928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + "'" + Phone.CONTENT_ITEM_TYPE + "', "
48214928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + "'" + SipAddress.CONTENT_ITEM_TYPE + "')"));
48224928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    frequentQuery = qb.buildQuery(subProjection, null, null, null, null, null);
48234928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                } else {
48244928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    setTablesAndProjectionMapForContacts(qb, uri, projection, true);
48254928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setProjectionMap(sStrequentFrequentProjectionMap);
48264928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.appendWhere(DbQueryUtils.concatenateClauses(
48274928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            selection,
48284928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            "(" + Contacts.STARRED + " =0 OR " + Contacts.STARRED + " IS NULL)",
48294928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            Contacts.IS_USER_PROFILE + "=0"));
48304928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    frequentQuery = qb.buildQuery(subProjection,
48314928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            null, Contacts._ID, null, null, null);
48324928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                }
4833d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
4834d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
48352f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final String unionQuery =
48362f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
48372f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                                STREQUENT_ORDER_BY, STREQUENT_LIMIT);
48382f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
48392f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Here, we need to use selection / selectionArgs (supplied from users) "twice",
48402f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // as we want them both for starred items and for frequently contacted items.
48412f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                //
48422f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // e.g. if the user specify selection = "starred =?" and selectionArgs = "0",
48432f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // the resultant SQL should be like:
48442f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // SELECT ... WHERE starred =? AND ...
48452f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // UNION ALL
48462f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // SELECT ... WHERE starred =? AND ...
48472f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                String[] doubledSelectionArgs = null;
48482f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (selectionArgs != null) {
48492f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    final int length = selectionArgs.length;
48502f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    doubledSelectionArgs = new String[length * 2];
48517d7d0e95636344c01eb4e4d034791c199bee98e9Daisuke Miyakawa                    System.arraycopy(selectionArgs, 0, doubledSelectionArgs, 0, length);
48527d7d0e95636344c01eb4e4d034791c199bee98e9Daisuke Miyakawa                    System.arraycopy(selectionArgs, 0, doubledSelectionArgs, length, length);
48532f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                }
48542f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
48552f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                Cursor cursor = db.rawQuery(unionQuery, doubledSelectionArgs);
48562f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (cursor != null) {
48572f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    cursor.setNotificationUri(getContext().getContentResolver(),
4858d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
4859d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
48602f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                return cursor;
4861d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
4862d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
486345ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa            case CONTACTS_FREQUENT: {
486445ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                setTablesAndProjectionMapForContacts(qb, uri, projection, true);
486545ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                qb.setProjectionMap(sStrequentFrequentProjectionMap);
4866363bdaba2994539e1a3a2342a9fcf223604d69eaDaisuke Miyakawa                qb.appendWhere(Contacts.IS_USER_PROFILE + "=0");
486745ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                groupBy = Contacts._ID;
486845ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                if (!TextUtils.isEmpty(sortOrder)) {
486945ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                    sortOrder = FREQUENT_ORDER_BY + ", " + sortOrder;
487045ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                } else {
487145ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                    sortOrder = FREQUENT_ORDER_BY;
487245ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                }
487345ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                break;
487445ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa            }
487545ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa
4876ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_GROUP: {
4877763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
4878b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
487971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
48804a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
4881b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
4882b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
4883b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
4884b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
488524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
488624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
488724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForContacts(qb, uri, projection);
488824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(Contacts.IS_USER_PROFILE + "=1");
488924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
489024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
489124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
489224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_ENTITIES: {
489324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
489424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForEntities(qb, uri, projection);
489524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + Contacts.IS_USER_PROFILE + "=1");
489624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
489724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
489824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
489924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA: {
490024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
490124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
490224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1");
490324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
490424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
490524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
490624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA_ID: {
490724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
490824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
490924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
491024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + Data._ID + "=? AND "
491124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1");
491224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
491324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
491424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
491524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD: {
491624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
4917ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
491824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.setProjectionMap(sContactsVCardProjectionMap);
491924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(Contacts.IS_USER_PROFILE + "=1");
492024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
492124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
492224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
4923a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_DATA: {
49244a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
492582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
49264da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
49274da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
49286bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
49296bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
493000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
4931a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
49323653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
4933afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
493482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
49354da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
49364da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
49373653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
49383653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                break;
49393653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
49403653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
4941a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_ENTITIES: {
4942a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
4943a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
4944a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
4945a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
4946a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
4947a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
4948a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4949a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ENTITIES:
4950a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_ENTITIES: {
4951a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
4952a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                int segmentCount = pathSegments.size();
4953a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount < 4) {
4954a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
4955a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            "Missing a lookup key", uri));
4956a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
4957a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
4958a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount == 5) {
4959a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
4960a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
4961a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    setTablesAndProjectionMapForEntities(lookupQb, uri, projection);
4962a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
4963a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4964a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
4965a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4966a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.CONTACT_ID, contactId,
4967a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.LOOKUP_KEY, lookupKey);
4968a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
4969a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        return c;
4970a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    }
4971a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
4972a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4973a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
4974a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
4975a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
4976a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + Contacts.Entity.CONTACT_ID + "=?");
4977a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
4978a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
4979a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
49803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
49813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
49823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
49833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
49843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
49853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
49863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
49873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
49883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItemsColumns.CONCRETE_ID + "=?");
49893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
49903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
49913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
49923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_LIMIT: {
49936802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                MatrixCursor cursor = new MatrixCursor(new String[]{StreamItems.MAX_ITEMS}, 1);
49946802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                cursor.addRow(new Object[]{MAX_STREAM_ITEMS_PER_RAW_CONTACT});
49953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return cursor;
49963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
49973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
49983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
49993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
50003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
50013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
50023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
50033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
50043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
50053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
50063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemId);
50073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=?");
50083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
50093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
50103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
50113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
50123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
50133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
50143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
50153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemPhotoId);
50163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemId);
50173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=? AND " +
50183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=?");
50193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
50203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
50213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
5022f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case PHOTO_DIMENSIONS: {
5023f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                MatrixCursor cursor = new MatrixCursor(
5024f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{DisplayPhoto.DISPLAY_MAX_DIM, DisplayPhoto.THUMBNAIL_MAX_DIM},
5025f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        1);
5026f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                cursor.addRow(new Object[]{mMaxDisplayPhotoDim, mMaxThumbnailPhotoDim});
5027f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                return cursor;
5028f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
5029f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
50304a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case PHONES: {
503182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
503289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
50332815f58f72f109790585931f601a63ddc02536a5Evan Millar                break;
50342815f58f72f109790585931f601a63ddc02536a5Evan Millar            }
50352815f58f72f109790585931f601a63ddc02536a5Evan Millar
503648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID: {
503782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
50384da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
503948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
50404da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
504148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
504248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
504348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
5044ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
504546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
50469245089f788c01597913b1e998be86a626ae6244Daisuke Miyakawa                profileRestrictionColumnName = RawContacts.RAW_CONTACT_IS_USER_PROFILE;
504746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                Integer typeInt = sDataUsageTypeMap.get(typeParam);
504846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (typeInt == null) {
504946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    typeInt = DataUsageStatColumns.USAGE_TYPE_INT_CALL;
505046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
505146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
505289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
5053ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
50544a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
50554a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
5056a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    sb.append(" AND (");
50575e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
505845d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    boolean hasCondition = false;
50595e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    boolean orNeeded = false;
50605e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String normalizedName = NameNormalizer.normalize(filterParam);
50615e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (normalizedName.length() > 0) {
5062155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN " +
5063155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
5064155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.SEARCH_INDEX +
5065155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " JOIN " + Tables.RAW_CONTACTS +
5066155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
5067155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
5068155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE " + SearchIndexColumns.NAME + " MATCH ");
50692352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                        DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filterParam) + "*");
5070155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(")");
50715e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        orNeeded = true;
507245d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
50735e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
50745e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
5075892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    String number = PhoneNumberUtils.normalizeNumber(filterParam);
5076892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (!TextUtils.isEmpty(number)) {
50775e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        if (orNeeded) {
50785e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                            sb.append(" OR ");
50795e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        }
50805e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data._ID +
5081892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                " IN (SELECT DISTINCT " + PhoneLookupColumns.DATA_ID
5082892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " FROM " + Tables.PHONE_LOOKUP
5083892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
5084892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append(number);
5085892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append("%')");
508645d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
508745d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    }
508845d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov
508945d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    if (!hasCondition) {
509045d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // If it is neither a phone number nor a name, the query should return
509145d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // an empty cursor.  Let's ensure that.
509245d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        sb.append("0");
50935e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
50945e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
5095a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
5096ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
509758567abca253f1efa2db5c39e17e42dca589e916Daisuke Miyakawa                groupBy = "(CASE WHEN " + PhoneColumns.NORMALIZED_NUMBER
509858567abca253f1efa2db5c39e17e42dca589e916Daisuke Miyakawa                        + " IS NOT NULL THEN " + PhoneColumns.NORMALIZED_NUMBER
509958567abca253f1efa2db5c39e17e42dca589e916Daisuke Miyakawa                        + " ELSE " + Phone.NUMBER + " END), " + RawContacts.CONTACT_ID;
5100a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
510146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
510246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
510346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = accountPromotionSortOrder + ", " + PHONE_FILTER_SORT_ORDER;
510446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    } else {
510546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = PHONE_FILTER_SORT_ORDER;
510646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    }
5107a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
5108ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
5109ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
5110ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
51114a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case EMAILS: {
511282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
511389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
51144a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                break;
51154a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
51164a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
511748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID: {
511882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
51194da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
51204da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'"
51214da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        + " AND " + Data._ID + "=?");
512248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
512348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
512448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
51255e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_LOOKUP: {
512682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
512789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
51284a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
512908768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String email = uri.getLastPathSegment();
513008768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String address = mDbHelper.extractAddressFromEmailAddress(email);
513108768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, address);
513208768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    qb.appendWhere(" AND UPPER(" + Email.DATA + ")=UPPER(?)");
51334a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
5134ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
5135ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
5136ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
51375e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_FILTER: {
513846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
51399245089f788c01597913b1e998be86a626ae6244Daisuke Miyakawa                profileRestrictionColumnName = RawContacts.RAW_CONTACT_IS_USER_PROFILE;
514046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                Integer typeInt = sDataUsageTypeMap.get(typeParam);
514146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (typeInt == null) {
514246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    typeInt = DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT;
514346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
514446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
514507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                String filterParam = null;
51467d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa
514707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
514807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    filterParam = uri.getLastPathSegment();
514907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    if (TextUtils.isEmpty(filterParam)) {
515007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                        filterParam = null;
515107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    }
515207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                }
51535e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
515407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (filterParam == null) {
515507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    // If the filter is unspecified, return nothing
515607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    qb.appendWhere(" AND 0");
515707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                } else {
515807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    StringBuilder sb = new StringBuilder();
515907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(" AND " + Data._ID + " IN (");
516007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(
516107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            "SELECT " + Data._ID +
516207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " FROM " + Tables.DATA +
51632a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                            " WHERE " + DataColumns.MIMETYPE_ID + "=");
51642a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                    sb.append(mDbHelper.getMimeTypeIdForEmail());
51652a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                    sb.append(" AND " + Data.DATA1 + " LIKE ");
516607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(sb, filterParam + '%');
516720938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                    if (!filterParam.contains("@")) {
5168155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(
5169155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " UNION SELECT " + Data._ID +
5170155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.DATA +
5171155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE +" + DataColumns.MIMETYPE_ID + "=");
5172155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(mDbHelper.getMimeTypeIdForEmail());
5173155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(" AND " + Data.RAW_CONTACT_ID + " IN " +
5174155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
5175155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.SEARCH_INDEX +
5176155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " JOIN " + Tables.RAW_CONTACTS +
5177155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
5178155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
5179155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE " + SearchIndexColumns.NAME + " MATCH ");
51802352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                        DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filterParam) + "*");
5181155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(")");
51825e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
51835e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
5184a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
51855e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                }
51865e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
5187a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
518846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
518946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
519046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = accountPromotionSortOrder + ", " + EMAIL_FILTER_SORT_ORDER;
51917d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                    } else {
51927d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                        sortOrder = EMAIL_FILTER_SORT_ORDER;
51937d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                    }
5194a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
51955e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                break;
51965e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
51975e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
5198ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
519982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
520089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
520189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
5202ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
5203ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
5204ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
520548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
520682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
52074da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
520848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
520948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
52104da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
521148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
521248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
521348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
52145ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
5215763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
52164b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                profileRestrictionColumnName = RawContacts.RAW_CONTACT_IS_USER_PROFILE;
52174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
52184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
52194f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
52205ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
52215ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
5222afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForRawContact(db, rawContactId, false);
5223763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
52244da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
52254da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
52264f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
52274f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
52284f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
52295ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
52305ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
523182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
52324da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
52334da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=?");
52344b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                profileRestrictionColumnName = RawContacts.RAW_CONTACT_IS_USER_PROFILE;
523524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
523624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
523724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
52383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case RAW_CONTACTS_ID_STREAM_ITEMS: {
52393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
5240afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForRawContact(db, rawContactId, false);
52413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
52423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
52433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItems.RAW_CONTACT_ID + "=?");
52443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
52453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
524624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
524724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS: {
524824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
524924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForRawContacts(qb, uri);
525024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1");
525124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
525224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
525324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
525424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID: {
525524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
525624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = ContentUris.parseId(uri);
525724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
525824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForRawContacts(qb, uri);
525924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1 AND "
526024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        + RawContacts._ID + "=?");
526124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
526224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
526324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
526424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID_DATA: {
526524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
526624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(2));
526724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
526824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
526924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1 AND "
527024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        + Data.RAW_CONTACT_ID + "=?");
527124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
527224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
527324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
527424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID_ENTITIES: {
527524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
527624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(2));
527724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
527824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForRawEntities(qb, uri);
527924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1 AND "
528024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        + RawContacts._ID + "=?");
5281e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
5282e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
5283e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
5284e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            case DATA: {
528582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
52864b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                profileRestrictionColumnName = RawContacts.RAW_CONTACT_IS_USER_PROFILE;
5287e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
5288e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
5289e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
52904f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case DATA_ID: {
529124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long dataId = ContentUris.parseId(uri);
5292afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForData(db, dataId, false);
529382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
52944da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
52954da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
5296a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                break;
5297a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            }
5298a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov
5299a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
53004a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
5301a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
5302a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
5303a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
5304892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    sortOrder = " length(lookup.normalized_number) DESC";
5305a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
5306a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
5307e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
5308e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                String numberE164 = PhoneNumberUtils.formatNumberToE164(number,
5309e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                        mDbHelper.getCurrentCountryIso());
5310892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                String normalizedNumber =
5311892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        PhoneNumberUtils.normalizeNumber(number);
53123f9ee8aaba20b358ca63f4b57523c960d82b5cc9Isaac Katzenelson                profileRestrictionColumnName = Contacts.IS_USER_PROFILE;
5313892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                mDbHelper.buildPhoneLookupAndContactQuery(qb, normalizedNumber, numberE164);
5314e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                qb.setProjectionMap(sPhoneLookupProjectionMap);
5315e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                // Phone lookup cannot be combined with a selection
5316e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selection = null;
5317e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selectionArgs = null;
5318a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
5319a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
5320a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
5321ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
5322ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.GROUPS);
5323ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
532443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                appendAccountFromParameter(qb, uri, true);
5325ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5326ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5327ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5328ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
5329ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.GROUPS);
5330ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
53314da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
53324da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Groups._ID + "=?");
5333ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5334ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5335ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5336ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
5337f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                final boolean returnGroupCountPerAccount =
5338f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        readBooleanQueryParameter(uri, Groups.PARAM_RETURN_GROUP_COUNT_PER_ACCOUNT,
5339f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                                false);
5340f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                qb.setTables(Views.GROUPS + " AS " + Tables.GROUPS);
5341f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                qb.setProjectionMap(returnGroupCountPerAccount ?
5342f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        sGroupsSummaryProjectionMapWithGroupCountPerAccount
5343f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        : sGroupsSummaryProjectionMap);
534443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                appendAccountFromParameter(qb, uri, true);
5345f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                groupBy = GroupsColumns.CONCRETE_ID;
5346ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5347ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5348ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5349b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
53500c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
5351b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
5352b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
5353b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
5354b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
535531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
5356d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
53572d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                String filter = null;
53582d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
53592d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                    filter = uri.getPathSegments().get(3);
53602d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                }
536131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
5362d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                if (limit != null) {
5363d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(limit);
536431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
536531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
536631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
536731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
53685b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                ArrayList<AggregationSuggestionParameter> parameters = null;
53695b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                List<String> query = uri.getQueryParameters("query");
53705b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                if (query != null && !query.isEmpty()) {
53715b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    parameters = new ArrayList<AggregationSuggestionParameter>(query.size());
53725b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    for (String parameter : query) {
53735b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        int offset = parameter.indexOf(':');
53745b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        parameters.add(offset == -1
53755b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                ? new AggregationSuggestionParameter(
537676dfa406e2cde19c824983c37fc92c1c5bf63eecDaniel Lehmann                                        AggregationSuggestions.PARAMETER_MATCH_NAME,
53775b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter)
53785b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                : new AggregationSuggestionParameter(
53795b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(0, offset),
53805b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(offset + 1)));
53815b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    }
53825b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                }
53835b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
5384763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
53857581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov
53867581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov                return mContactAggregator.queryAggregationSuggestions(qb, projection, contactId,
53875b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        maxSuggestions, filter, parameters);
538831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
538931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
5390eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
5391eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setTables(Tables.SETTINGS);
5392eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setProjectionMap(sSettingsProjectionMap);
539343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                appendAccountFromParameter(qb, uri, false);
5394e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
5395e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // When requesting specific columns, this query requires
5396e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // late-binding of the GroupMembership MIME-type.
5397b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                final String groupMembershipMimetypeId = Long.toString(mDbHelper
5398e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
539982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
5400b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_COUNT)) {
5401e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
5402e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
540382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
5404b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_WITH_PHONES)) {
5405e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
5406e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
5407e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
5408eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
5409eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
5410eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
541182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
54120a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
54135ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
54145ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
54155ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
541682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES_ID: {
54170a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
54184da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
54194da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(DataColumns.CONCRETE_ID + "=?");
54205ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
54215ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
54225ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
5423c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS: {
5424174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(
5425174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        db, uri, projection, limit);
5426c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5427c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
5428c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT: {
54292d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                String lookupKey = uri.getLastPathSegment();
5430174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                String filter = getQueryParameter(
5431174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        uri, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
5432174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchShortcutRefresh(
5433174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        db, projection, lookupKey, filter);
5434c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5435c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
54361b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS:
5437ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
54381b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
54391b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
54401b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
54411b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_WITH_PHONES:
5442ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
54431b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
54441b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.HAS_PHONE_NUMBER + "=1");
54451b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
54461b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
54471b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_FAVORITES:
5448ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
54491b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
54501b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.STARRED + "=1");
54511b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
54521b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
54531b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_GROUP_NAME:
5454ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
54551b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
545671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
54571b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
54581b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
54591b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
546046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITIES: {
5461a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
546246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
546346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
546446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
546546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITY_ID: {
546646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
5467a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
54684da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
54694da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
547046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
547146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
547246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
547309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            case PROVIDER_STATUS: {
547409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                return queryProviderStatus(uri, projection);
547509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
547609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5477d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES : {
5478d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
5479d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
5480d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
5481d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
5482d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
5483d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID : {
5484385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                long id = ContentUris.parseId(uri);
5485d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
5486d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
5487385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(id));
5488d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.appendWhere(Directory._ID + "=?");
5489d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
5490d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
5491d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
54927a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            case COMPLETE_NAME: {
54937a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                return completeName(uri, projection);
54947a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
54957a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
54964f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
5497f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
5498c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                        sortOrder, limit);
54994f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
55004f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
550109e69522745551522c55dff27424496f255def46Daniel Lehmann        qb.setStrict(true);
55027f786e5cbde9975b9632beb9b6d19eeef8a64cf1Dmitri Plotnikov
55034b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki        if (profileRestrictionColumnName != null) {
55044b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki            // This check is very slow and most of the rows will pass though this check, so
55054b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki            // it should be put after user's selection, so SQLite won't do this check first.
55064b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki            selection = appendProfileRestriction(uri, profileRestrictionColumnName,
55074b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                    suppressProfileCheck, selection);
55084b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki        }
55094b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki
5510ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        Cursor cursor =
5511ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit);
5512ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (readBooleanQueryParameter(uri, ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, false)) {
5513ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            cursor = bundleLetterCountExtras(cursor, db, qb, selection, selectionArgs, sortOrder);
5514ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5515ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        return cursor;
55165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
55175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
55185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
55195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String selection, String[] selectionArgs, String sortOrder, String groupBy,
55205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String limit) {
5521038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        if (projection != null && projection.length == 1
5522038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                && BaseColumns._COUNT.equals(projection[0])) {
5523038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            qb.setProjectionMap(sCountProjectionMap);
5524038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
55255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
55265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sortOrder, limit);
55274f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
55284f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
55294f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
55304f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
55314f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
55324f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
553309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    /**
553409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     * Creates a single-row cursor containing the current status of the provider.
553509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     */
553609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private Cursor queryProviderStatus(Uri uri, String[] projection) {
553709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
553809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        RowBuilder row = cursor.newRow();
553909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
554009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            if (ProviderStatus.STATUS.equals(projection[i])) {
554109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mProviderStatus);
554209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            } else if (ProviderStatus.DATA1.equals(projection[i])) {
554309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mEstimatedStorageRequirement);
554409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
554509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        }
554609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        return cursor;
554709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    }
554809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5549a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /**
5550a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * Runs the query with the supplied contact ID and lookup ID.  If the query succeeds,
5551a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * it returns the resulting cursor, otherwise it returns null and the calling
5552a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * method needs to resolve the lookup key and rerun the query.
5553a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     */
5554a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private Cursor queryWithContactIdAndLookupKey(SQLiteQueryBuilder lookupQb,
5555a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            SQLiteDatabase db, Uri uri,
5556a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection, String selection, String[] selectionArgs,
5557a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String sortOrder, String groupBy, String limit,
5558a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn, long contactId, String lookupKeyColumn, String lookupKey) {
5559a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        String[] args;
5560a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (selectionArgs == null) {
5561a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[2];
5562a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        } else {
5563a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[selectionArgs.length + 2];
5564a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
5565a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5566a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[0] = String.valueOf(contactId);
5567a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[1] = Uri.encode(lookupKey);
5568a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        lookupQb.appendWhere(contactIdColumn + "=? AND " + lookupKeyColumn + "=?");
5569a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        Cursor c = query(db, lookupQb, projection, selection, args, sortOrder,
5570a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                groupBy, limit);
5571a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (c.getCount() != 0) {
5572a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return c;
5573a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5574a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5575a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        c.close();
5576a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return null;
5577a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
557809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5579bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov    private static final class AddressBookIndexQuery {
5580bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String LETTER = "letter";
5581bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String TITLE = "title";
5582bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String COUNT = "count";
5583ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5584bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
5585bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                LETTER, TITLE, COUNT
5586ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        };
5587ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5588bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_LETTER = 0;
5589bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_TITLE = 1;
5590bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_COUNT = 2;
5591bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
559224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // The first letter of the sort key column is what is used for the index headings, except
559324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // in the case of the user's profile, in which case it is empty.
559424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        public static final String SECTION_HEADING_TEMPLATE =
559524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                "(CASE WHEN %1$s=1 THEN '' ELSE SUBSTR(%2$s,1,1) END)";
559624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
5597de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa        public static final String ORDER_BY = LETTER + " COLLATE " + PHONEBOOK_COLLATOR_NAME;
5598ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
5599ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5600ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    /**
5601ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * Computes counts by the address book index titles and adds the resulting tally
5602ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * to the returned cursor as a bundle of extras.
5603ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     */
5604ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    private Cursor bundleLetterCountExtras(Cursor cursor, final SQLiteDatabase db,
5605ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            SQLiteQueryBuilder qb, String selection, String[] selectionArgs, String sortOrder) {
5606ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortKey;
5607ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5608ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // The sort order suffix could be something like "DESC".
5609ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // We want to preserve it in the query even though we will change
5610ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // the sort column itself.
5611ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortOrderSuffix = "";
5612ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (sortOrder != null) {
561324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
561424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // If the sort order contains one of the "is_profile" columns, we need to strip it out
561524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // first.
561624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            if (sortOrder.contains(Contacts.IS_USER_PROFILE)
561724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    || sortOrder.contains(RawContacts.RAW_CONTACT_IS_USER_PROFILE)) {
561824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                String[] splitOrderClauses = sortOrder.split(",");
561924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                StringBuilder rejoinedClause = new StringBuilder();
562024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                for (String orderClause : splitOrderClauses) {
562124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    if (!orderClause.contains(Contacts.IS_USER_PROFILE)
562224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                            && !orderClause.contains(RawContacts.RAW_CONTACT_IS_USER_PROFILE)) {
562324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        if (rejoinedClause.length() > 0) {
562424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                            rejoinedClause.append(", ");
562524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        }
562624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        rejoinedClause.append(orderClause.trim());
562724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    }
562824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                }
562924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                sortOrder = rejoinedClause.toString();
563024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
563124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
5632ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int spaceIndex = sortOrder.indexOf(' ');
5633ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            if (spaceIndex != -1) {
5634ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder.substring(0, spaceIndex);
5635ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortOrderSuffix = sortOrder.substring(spaceIndex);
5636ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            } else {
5637ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder;
5638ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
5639ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } else {
5640ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            sortKey = Contacts.SORT_KEY_PRIMARY;
5641ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5642ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5643bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        String locale = getLocale().toString();
5644ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        HashMap<String, String> projectionMap = Maps.newHashMap();
564524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
564624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // The user profile column varies depending on the view.
5647ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        String profileColumn = qb.getTables().contains(Views.CONTACTS)
564824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                ? Contacts.IS_USER_PROFILE
564924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                : RawContacts.RAW_CONTACT_IS_USER_PROFILE;
565024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        String sectionHeading = String.format(
565124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                AddressBookIndexQuery.SECTION_HEADING_TEMPLATE, profileColumn, sortKey);
5652bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.LETTER,
565324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                sectionHeading + " AS " + AddressBookIndexQuery.LETTER);
5654bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5655bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        /**
5656bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * Use the GET_PHONEBOOK_INDEX function, which is an android extension for SQLite3,
5657bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * to map the first letter of the sort key to a character that is traditionally
5658bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * used in phonebooks to represent that letter.  For example, in Korean it will
5659bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * be the first consonant in the letter; for Japanese it will be Hiragana rather
5660bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * than Katakana.
5661bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         */
5662ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.TITLE,
566324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                "GET_PHONEBOOK_INDEX(" + sectionHeading + ",'" + locale + "')"
5664bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                        + " AS " + AddressBookIndexQuery.TITLE);
5665ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.COUNT,
5666ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                "COUNT(" + Contacts._ID + ") AS " + AddressBookIndexQuery.COUNT);
5667ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        qb.setProjectionMap(projectionMap);
5668ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5669f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov        Cursor indexCursor = qb.query(db, AddressBookIndexQuery.COLUMNS, selection, selectionArgs,
5670ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY, null /* having */,
5671ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY + sortOrderSuffix);
5672ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5673ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        try {
5674f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            int groupCount = indexCursor.getCount();
5675ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            String titles[] = new String[groupCount];
5676ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int counts[] = new int[groupCount];
5677bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            int indexCount = 0;
5678bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            String currentTitle = null;
5679bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5680bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // Since GET_PHONEBOOK_INDEX is a many-to-1 function, we may end up
5681bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // with multiple entries for the same title.  The following code
5682bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // collapses those duplicates.
5683ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            for (int i = 0; i < groupCount; i++) {
5684f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov                indexCursor.moveToNext();
5685bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String title = indexCursor.getString(AddressBookIndexQuery.COLUMN_TITLE);
5686bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int count = indexCursor.getInt(AddressBookIndexQuery.COLUMN_COUNT);
5687bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                if (indexCount == 0 || !TextUtils.equals(title, currentTitle)) {
5688bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    titles[indexCount] = currentTitle = title;
5689bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount] = count;
5690bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    indexCount++;
5691bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                } else {
5692bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount - 1] += count;
5693bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                }
5694bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            }
5695bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5696bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            if (indexCount < groupCount) {
5697bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String[] newTitles = new String[indexCount];
5698bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(titles, 0, newTitles, 0, indexCount);
5699bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                titles = newTitles;
5700bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5701bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int[] newCounts = new int[indexCount];
5702bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(counts, 0, newCounts, 0, indexCount);
5703bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                counts = newCounts;
5704ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
5705ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5706e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return new AddressBookCursor((CrossProcessCursor) cursor, titles, counts);
5707ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } finally {
5708f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            indexCursor.close();
5709ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5710ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
5711ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
57122d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    /**
571392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Returns the contact Id for the contact identified by the lookupKey.
571492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Robust against changes in the lookup key: if the key has changed, will
571592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * look up the contact by the raw contact IDs or name encoded in the lookup
571692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * key.
57172d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     */
57182d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    public long lookupContactIdByLookupKey(SQLiteDatabase db, String lookupKey) {
57195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactLookupKey key = new ContactLookupKey();
57205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ArrayList<LookupKeySegment> segments = key.parse(lookupKey);
57215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
572292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        long contactId = -1;
572392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_SOURCE_ID)) {
572492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdBySourceIds(db, segments);
572592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
572692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
572792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
572892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
572992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
573092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        boolean hasRawContactIds =
573192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID);
573292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds) {
573392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdByRawContactIds(db, segments);
573492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
573592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
573692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
573792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
573892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
573992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds
574092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                || lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME)) {
57415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            contactId = lookupContactIdByDisplayNames(db, segments);
57425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
57435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
57445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return contactId;
57455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
57465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
57475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupBySourceIdQuery {
574843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String TABLE = Views.RAW_CONTACTS;
57495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
57505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
57515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
575243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
57535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
57545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.SOURCE_ID
57555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
57565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
57575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
575843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int ACCOUNT_TYPE_AND_DATA_SET = 1;
57595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
57605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int SOURCE_ID = 3;
57615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
57625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
57635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdBySourceIds(SQLiteDatabase db,
57645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
57655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
57665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(RawContacts.SOURCE_ID + " IN (");
57675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
57685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
576992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID) {
57705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
57715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
57725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
57735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
57745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
57755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
57765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
57775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupBySourceIdQuery.TABLE, LookupBySourceIdQuery.COLUMNS,
57785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
57795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
57805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
578143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                String accountTypeAndDataSet =
578243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE_AND_DATA_SET);
57835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME);
57845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
578543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        ContactLookupKey.getAccountHashCode(accountTypeAndDataSet, accountName);
57865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID);
57875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
57885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
578992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID
579092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
57915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(sourceId)) {
57925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupBySourceIdQuery.CONTACT_ID);
57935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
57945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
57955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
57965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
57975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
57985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
57995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
58005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
58015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
58025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
58035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
580492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByRawContactIdQuery {
580543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String TABLE = Views.RAW_CONTACTS;
58065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
58075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
58085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
580943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
58105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
581192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts._ID,
58125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
58135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
58145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
581543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int ACCOUNT_TYPE_AND_DATA_SET = 1;
58165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
581792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ID = 3;
58185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
58195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
582092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByRawContactIds(SQLiteDatabase db,
582192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            ArrayList<LookupKeySegment> segments) {
582292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
582392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(RawContacts._ID + " IN (");
58245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
58255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
582692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
582792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(segment.rawContactId);
582892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(",");
58295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
58305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
583192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
583292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
58335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
583492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        Cursor c = db.query(LookupByRawContactIdQuery.TABLE, LookupByRawContactIdQuery.COLUMNS,
583592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                 sb.toString(), null, null, null, null);
583692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        try {
583792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            while (c.moveToNext()) {
583843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                String accountTypeAndDataSet = c.getString(
583943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        LookupByRawContactIdQuery.ACCOUNT_TYPE_AND_DATA_SET);
584092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountName = c.getString(LookupByRawContactIdQuery.ACCOUNT_NAME);
584192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                int accountHashCode =
584243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        ContactLookupKey.getAccountHashCode(accountTypeAndDataSet, accountName);
584392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String rawContactId = c.getString(LookupByRawContactIdQuery.ID);
584492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
584592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
584692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID
584792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
584892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && segment.rawContactId.equals(rawContactId)) {
584992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        segment.contactId = c.getLong(LookupByRawContactIdQuery.CONTACT_ID);
585092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        break;
585192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    }
585292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                }
585392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
585492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        } finally {
585592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            c.close();
58565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
58575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
585892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return getMostReferencedContactId(segments);
585992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
586092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
586192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByDisplayNameQuery {
586292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
586392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
586492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String COLUMNS[] = {
586592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.CONTACT_ID,
586643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
586792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
586892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME
586992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        };
587092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
587192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int CONTACT_ID = 0;
587243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int ACCOUNT_TYPE_AND_DATA_SET = 1;
587392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_NAME = 2;
587492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int NORMALIZED_NAME = 3;
587592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
587692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
587792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByDisplayNames(SQLiteDatabase db,
587892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
58795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
58805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(NameLookupColumns.NORMALIZED_NAME + " IN (");
58815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
58825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
588392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
588492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
58855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
58865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
58875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
58885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
58895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
58905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
58915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                + " AND " + RawContacts.CONTACT_ID + " NOT NULL");
58925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
58935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupByDisplayNameQuery.TABLE, LookupByDisplayNameQuery.COLUMNS,
58945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
58955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
58965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
589743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                String accountTypeAndDataSet =
589843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE_AND_DATA_SET);
58995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME);
59005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
590143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        ContactLookupKey.getAccountHashCode(accountTypeAndDataSet, accountName);
59025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME);
59035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
59045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
590592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if ((segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
590692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID)
590792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
59085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(name)) {
59095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupByDisplayNameQuery.CONTACT_ID);
59105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
59115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
59125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
59135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
59145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
59155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
59165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
59175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
59185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
59195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
59205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
592192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private boolean lookupKeyContainsType(ArrayList<LookupKeySegment> segments, int lookupType) {
592292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
592392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            LookupKeySegment segment = segments.get(i);
592492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == lookupType) {
592592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return true;
592692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
592792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
592892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
592992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return false;
593092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
593192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
5932ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateLookupKeyForRawContact(SQLiteDatabase db, long rawContactId) {
5933ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov        mContactAggregator.updateLookupKeyForRawContact(db, rawContactId);
5934ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    }
5935ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov
59365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /**
59375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     * Returns the contact ID that is mentioned the highest number of times.
59385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     */
59395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long getMostReferencedContactId(ArrayList<LookupKeySegment> segments) {
59405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Collections.sort(segments);
59415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
59425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long bestContactId = -1;
59435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int bestRefCount = 0;
59445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
59455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = -1;
59465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int count = 0;
59475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
59485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int segmentCount = segments.size();
59495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segmentCount; i++) {
59505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
59515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.contactId != -1) {
59525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segment.contactId == contactId) {
59535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count++;
59545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                } else {
59555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (count > bestRefCount) {
59565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestContactId = contactId;
59575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestRefCount = count;
59585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
59595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    contactId = segment.contactId;
59605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count = 1;
59615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
59625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
59635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
59645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count > bestRefCount) {
59655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return contactId;
59665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
59675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return bestContactId;
59685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
59695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
59705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
5971763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
5972763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            String[] projection) {
59734928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa        setTablesAndProjectionMapForContacts(qb, uri, projection, false);
59742f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    }
59752f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
59762f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    /**
59774928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * @param includeDataUsageStat true when the table should include DataUsageStat table.
59784928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * Note that this uses INNER JOIN instead of LEFT OUTER JOIN, so some of data in Contacts
59794928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * may be dropped.
59802f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     */
59812f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
59824928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            String[] projection, boolean includeDataUsageStat) {
598382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5984ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.CONTACTS);
59852f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
59862f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa        // Just for frequently contacted contacts in Strequent Uri handling.
59874928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa        if (includeDataUsageStat) {
59882f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            sb.append(" INNER JOIN " +
5989ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                    Views.DATA_USAGE_STAT + " AS " + Tables.DATA_USAGE_STAT +
59902f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    " ON (" +
59912f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    DbQueryUtils.concatenateClauses(
59922f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                            DataUsageStatColumns.CONCRETE_TIMES_USED + " > 0",
59934928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            RawContacts.CONTACT_ID + "=" + Views.CONTACTS + "." + Contacts._ID) +
59942f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    ")");
59952f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa        }
59962f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
59977ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
59987ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
5999916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setTables(sb.toString());
6000916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionMap);
6001916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    }
6002916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
6003916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /**
6004916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * Finds name lookup records matching the supplied filter, picks one arbitrary match per
6005916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * contact and joins that with other contacts tables.
6006916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     */
6007916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private void setTablesAndProjectionMapForContactsWithSnippet(SQLiteQueryBuilder qb, Uri uri,
60087ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov            String[] projection, String filter, long directoryId) {
60097ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov
60107ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
6011ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.CONTACTS);
6012916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
601303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        if (filter != null) {
601403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            filter = filter.trim();
601503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        }
601603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
601730cc766756461da8d53933f88ea01dd2272a90ebDmitri Plotnikov        if (TextUtils.isEmpty(filter) || (directoryId != -1 && directoryId != Directory.DEFAULT)) {
601830cc766756461da8d53933f88ea01dd2272a90ebDmitri Plotnikov            sb.append(" JOIN (SELECT NULL AS " + SearchSnippetColumns.SNIPPET + " WHERE 0)");
60195e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        } else {
60205e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            appendSearchIndexJoin(sb, uri, projection, filter);
60215e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        }
60227ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
60237ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
602403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        qb.setTables(sb.toString());
602503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionWithSnippetMap);
602603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    }
6027916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
602803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private void appendSearchIndexJoin(
602903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            StringBuilder sb, Uri uri, String[] projection, String filter) {
6030916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
6031174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET)) {
603203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String[] args = null;
603303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String snippetArgs =
603403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                    getQueryParameter(uri, SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY);
603503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            if (snippetArgs != null) {
603603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                args = snippetArgs.split(",");
603703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
603803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
60395e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String startMatch = args != null && args.length > 0 ? args[0]
60405e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_START_MATCH;
60415e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String endMatch = args != null && args.length > 1 ? args[1]
60425e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_END_MATCH;
60435e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String ellipsis = args != null && args.length > 2 ? args[2]
60445e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_ELLIPSIS;
60455e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            int maxTokens = args != null && args.length > 3 ? Integer.parseInt(args[3])
60465e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_MAX_TOKENS;
60475e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
6048174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            appendSearchIndexJoin(
6049174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                    sb, filter, true, startMatch, endMatch, ellipsis, maxTokens);
6050174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        } else {
6051174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            appendSearchIndexJoin(sb, filter, false, null, null, null, 0);
6052174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
6053174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    }
6054174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
6055174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    public void appendSearchIndexJoin(StringBuilder sb, String filter,
6056174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            boolean snippetNeeded, String startMatch, String endMatch, String ellipsis,
6057174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            int maxTokens) {
6058174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        boolean isEmailAddress = false;
6059174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String emailAddress = null;
6060174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        boolean isPhoneNumber = false;
6061174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String phoneNumber = null;
6062174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String numberE164 = null;
6063174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
60643716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // If the query consists of a single word, we can do snippetizing after-the-fact for a
60653716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // performance boost.
60663716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        boolean singleTokenSearch = filter.split(QUERY_TOKENIZER_REGEX).length == 1;
60673716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
6068174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (filter.indexOf('@') != -1) {
6069174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            emailAddress = mDbHelper.extractAddressFromEmailAddress(filter);
6070174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            isEmailAddress = !TextUtils.isEmpty(emailAddress);
6071174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        } else {
6072174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            isPhoneNumber = isPhoneNumber(filter);
607304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            if (isPhoneNumber) {
607404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                phoneNumber = PhoneNumberUtils.normalizeNumber(filter);
607504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                numberE164 = PhoneNumberUtils.formatNumberToE164(phoneNumber,
607604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                        mDbHelper.getCountryIso());
607704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            }
6078174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
6079174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
6080174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        sb.append(" JOIN (SELECT " + SearchIndexColumns.CONTACT_ID + " AS snippet_contact_id");
6081174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (snippetNeeded) {
60825e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            sb.append(", ");
60835e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            if (isEmailAddress) {
60843d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append("ifnull(");
60855e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, startMatch);
608604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("||(SELECT MIN(" + Email.ADDRESS + ")");
608704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" FROM " + Tables.DATA_JOIN_RAW_CONTACTS);
608804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" WHERE  " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
608904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + RawContacts.CONTACT_ID + " AND " + Email.ADDRESS + " LIKE ");
609004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                DatabaseUtils.appendEscapedSQLString(sb, filter + "%");
609104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(")||");
60923d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, endMatch);
60933d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append(",");
60943716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
60953716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                // Optimization for single-token search.
60963716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                if (singleTokenSearch) {
60973716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    sb.append(SearchIndexColumns.CONTENT);
60983716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                } else {
60993716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
61003716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                }
61013d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append(")");
61023d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov            } else if (isPhoneNumber) {
61033d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append("ifnull(");
61043d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, startMatch);
610504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("||(SELECT MIN(" + Phone.NUMBER + ")");
610604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" FROM " +
610704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                        Tables.DATA_JOIN_RAW_CONTACTS + " JOIN " + Tables.PHONE_LOOKUP);
610804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" ON " + DataColumns.CONCRETE_ID);
610904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + Tables.PHONE_LOOKUP + "." + PhoneLookupColumns.DATA_ID);
611004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" WHERE  " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
611104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + RawContacts.CONTACT_ID);
611204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" AND " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
611304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(phoneNumber);
611404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("%'");
611504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                if (!TextUtils.isEmpty(numberE164)) {
611604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append(" OR " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
611704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append(numberE164);
611804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append("%'");
611904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                }
612004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(")||");
61215e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, endMatch);
61225e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                sb.append(",");
61233716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
61243716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                // Optimization for single-token search.
61253716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                if (singleTokenSearch) {
61263716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    sb.append(SearchIndexColumns.CONTENT);
61273716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                } else {
61283716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
61293716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                }
61305e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                sb.append(")");
613103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            } else {
613204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                final String normalizedFilter = NameNormalizer.normalize(filter);
613304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                if (!TextUtils.isEmpty(normalizedFilter)) {
61343716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    // Optimization for single-token search.
61353716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    if (singleTokenSearch) {
61363716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(SearchIndexColumns.CONTENT);
61373716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    } else {
61383716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("(CASE WHEN EXISTS (SELECT 1 FROM ");
61393716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.RAW_CONTACTS + " AS rc INNER JOIN ");
61403716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.NAME_LOOKUP + " AS nl ON (rc." + RawContacts._ID);
61413716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("=nl." + NameLookupColumns.RAW_CONTACT_ID);
61423716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(") WHERE nl." + NameLookupColumns.NORMALIZED_NAME);
61433716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(" GLOB '" + normalizedFilter + "*' AND ");
61443716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("nl." + NameLookupColumns.NAME_TYPE + "=");
61453716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(NameLookupType.NAME_COLLATION_KEY + " AND ");
61463716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
61473716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("=rc." + RawContacts.CONTACT_ID);
61483716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(") THEN NULL ELSE ");
61493716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
61503716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(" END)");
61513716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    }
615204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                } else {
615304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append("NULL");
615404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                }
615503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
61565e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            sb.append(" AS " + SearchSnippetColumns.SNIPPET);
61575e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        }
615803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
61595e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(" FROM " + Tables.SEARCH_INDEX);
61605e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(" WHERE ");
61615e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(Tables.SEARCH_INDEX + " MATCH ");
61625e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        if (isEmailAddress) {
61632352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb, "\"" + sanitizeMatch(filter) + "*\"");
61643d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov        } else if (isPhoneNumber) {
61652352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb,
616604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    "\"" + sanitizeMatch(filter) + "*\" OR \"" + phoneNumber + "*\""
61672352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                            + (numberE164 != null ? " OR \"" + numberE164 + "\"" : ""));
616803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        } else {
61692352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filter) + "*");
61709c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        }
617103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        sb.append(") ON (" + Contacts._ID + "=snippet_contact_id)");
6172a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov    }
6173a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov
61742352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov    private String sanitizeMatch(String filter) {
61752352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov        // TODO more robust preprocessing of match expressions
61762352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov        return filter.replace('-', ' ').replace('\"', ' ');
61772352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov    }
61782352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov
61795e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov    private void appendSnippetFunction(
61805e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            StringBuilder sb, String startMatch, String endMatch, String ellipsis, int maxTokens) {
61815e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append("snippet(" + Tables.SEARCH_INDEX + ",");
61825e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, startMatch);
61835e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",");
61845e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, endMatch);
61855e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",");
61865e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, ellipsis);
61875e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
61885e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        // The index of the column used for the snippet, "content"
61895e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",1,");
61905e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(maxTokens);
61915e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(")");
61925e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov    }
61935e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
6194763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForRawContacts(SQLiteQueryBuilder qb, Uri uri) {
6195763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        StringBuilder sb = new StringBuilder();
6196ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.RAW_CONTACTS);
6197763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setTables(sb.toString());
6198763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setProjectionMap(sRawContactsProjectionMap);
619943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        appendAccountFromParameter(qb, uri, true);
6200763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    }
6201763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
6202a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForRawEntities(SQLiteQueryBuilder qb, Uri uri) {
6203ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        qb.setTables(Views.RAW_ENTITIES);
6204a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sRawEntityProjectionMap);
620543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        appendAccountFromParameter(qb, uri, true);
620646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    }
620746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
620882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
620982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String[] projection, boolean distinct) {
621046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        setTablesAndProjectionMapForData(qb, uri, projection, distinct, null);
621146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
621246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
621346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
621446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * @param usageType when non-null {@link Tables#DATA_USAGE_STAT} is joined with the specified
621546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * type.
621646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
621746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
621846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            String[] projection, boolean distinct, Integer usageType) {
621982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
6220ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.DATA);
622182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sb.append(" data");
622282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
6223a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, RawContacts.CONTACT_ID);
6224a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
6225a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
6226a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
62273296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
622846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (usageType != null) {
622946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            appendDataUsageStatJoin(sb, usageType, DataColumns.CONCRETE_ID);
623046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
623146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
623282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
6233f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov
6234f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        boolean useDistinct = distinct
6235f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov                || !mDbHelper.isInProjection(projection, DISTINCT_DATA_PROHIBITING_COLUMNS);
6236f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setDistinct(useDistinct);
6237f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setProjectionMap(useDistinct ? sDistinctDataProjectionMap : sDataProjectionMap);
623843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        appendAccountFromParameter(qb, uri, true);
6239ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    }
6240ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
62410a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb,
62420a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String[] projection) {
62430a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        StringBuilder sb = new StringBuilder();
6244ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.DATA);
62450a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        sb.append(" data");
6246a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
6247a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
62480a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
6249a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
6250a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sStatusUpdatesProjectionMap);
6251a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6252a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
62533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void setTablesAndProjectionMapForStreamItems(SQLiteQueryBuilder qb) {
62541dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro        qb.setTables(Tables.STREAM_ITEMS
62551dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + " JOIN " + Tables.RAW_CONTACTS + " ON ("
62561dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + StreamItemsColumns.CONCRETE_RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID
62571dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + ") JOIN " + Tables.CONTACTS + " ON ("
62581dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + RawContactsColumns.CONCRETE_CONTACT_ID + "=" + ContactsColumns.CONCRETE_ID + ")");
62593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        qb.setProjectionMap(sStreamItemsProjectionMap);
62603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
62613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
62623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void setTablesAndProjectionMapForStreamItemPhotos(SQLiteQueryBuilder qb) {
62631dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro        qb.setTables(Tables.PHOTO_FILES
62641dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + " JOIN " + Tables.STREAM_ITEM_PHOTOS + " ON ("
62651dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + StreamItemPhotosColumns.CONCRETE_PHOTO_FILE_ID + "="
62661dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + PhotoFilesColumns.CONCRETE_ID
62671dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + ") JOIN " + Tables.STREAM_ITEMS + " ON ("
62681dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "="
62690bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                + StreamItemsColumns.CONCRETE_ID + ")"
62700bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                + " JOIN " + Tables.RAW_CONTACTS + " ON ("
62710bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                + StreamItemsColumns.CONCRETE_RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID
62720bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                + ")");
62733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        qb.setProjectionMap(sStreamItemPhotosProjectionMap);
62743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
62753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
6276a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForEntities(SQLiteQueryBuilder qb, Uri uri,
6277a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection) {
6278a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
6279ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.ENTITIES);
6280a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(" data");
6281a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6282a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts.Entity.CONTACT_ID);
6283a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
6284a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, Contacts.Entity.DATA_ID);
6285a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, Contacts.Entity.DATA_ID);
6286a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6287a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
6288a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sEntityProjectionMap);
628943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        appendAccountFromParameter(qb, uri, true);
6290a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6291a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6292a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactStatusUpdateJoin(StringBuilder sb, String[] projection,
6293a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String lastStatusUpdateIdColumn) {
6294a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection,
6295a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS,
6296a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
6297a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
6298a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
6299a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP)) {
6300a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
6301a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    + ContactsStatusUpdatesColumns.ALIAS +
6302a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + lastStatusUpdateIdColumn + "="
6303a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
63040a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
6305a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
63060a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
6307a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataStatusUpdateJoin(StringBuilder sb, String[] projection,
6308a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
6309b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
63100a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS,
63110a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_RES_PACKAGE,
63120a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_ICON,
63130a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_LABEL,
63140a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_TIMESTAMP)) {
63150a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
6316a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdatesColumns.CONCRETE_DATA_ID + "="
6317a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + dataIdColumn + ")");
63180a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
6319a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6320a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
632146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private void appendDataUsageStatJoin(StringBuilder sb, int usageType, String dataIdColumn) {
632246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        sb.append(" LEFT OUTER JOIN " + Tables.DATA_USAGE_STAT +
632346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "=" + dataIdColumn +
632446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                " AND " + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "=" + usageType + ")");
632546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
632646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
6327a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactPresenceJoin(StringBuilder sb, String[] projection,
6328a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn) {
6329a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection,
6330a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_PRESENCE, Contacts.CONTACT_CHAT_CAPABILITY)) {
6331a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
6332a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + contactIdColumn + " = "
6333a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + AggregatedPresenceColumns.CONCRETE_CONTACT_ID + ")");
6334a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
6335a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6336a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6337a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataPresenceJoin(StringBuilder sb, String[] projection,
6338a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
6339a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, Data.PRESENCE, Data.CHAT_CAPABILITY)) {
6340a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
6341a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdates.DATA_ID + "=" + dataIdColumn + ")");
6342a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
6343a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6344a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
634524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private boolean appendLocalDirectorySelectionIfNeeded(SQLiteQueryBuilder qb, long directoryId) {
6346385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directoryId == Directory.DEFAULT) {
6347385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " IN " + Tables.DEFAULT_DIRECTORY);
634824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            return true;
6349385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directoryId == Directory.LOCAL_INVISIBLE){
6350385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " NOT IN " + Tables.DEFAULT_DIRECTORY);
635124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            return true;
635224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
635324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        return false;
635424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
635524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
63564b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki    private String appendProfileRestriction(Uri uri, String profileColumn,
63574b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki            boolean suppressProfileCheck, String originalSelection) {
63584b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki        if (shouldIncludeProfile(uri, suppressProfileCheck)) {
63594b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki            return originalSelection;
63604b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki        } else {
63614b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki            final String SELECTION = "(" + profileColumn + " IS NULL OR " + profileColumn + "=0)";
63624b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki            if (TextUtils.isEmpty(originalSelection)) {
63634b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                return SELECTION;
63644b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki            } else {
63654b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                StringBuilder sb = new StringBuilder();
63664b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                sb.append("(");
63674b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                sb.append(originalSelection);
63684b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                sb.append(") AND ");
63694b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                sb.append(SELECTION);
63704b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                return sb.toString();
63714b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki            }
6372385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        }
6373385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov    }
6374385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov
63756ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro    private String prependProfileSortIfNeeded(Uri uri, String sortOrder,
63766ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro            boolean suppressProfileCheck) {
63776ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro        if (shouldIncludeProfile(uri, suppressProfileCheck)) {
637824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            if (TextUtils.isEmpty(sortOrder)) {
637924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                return Contacts.IS_USER_PROFILE + " DESC";
638024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            } else {
638124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                return Contacts.IS_USER_PROFILE + " DESC, " + sortOrder;
638224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
638324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
638424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        return sortOrder;
638524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
638624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
63876ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro    private boolean shouldIncludeProfile(Uri uri, boolean suppressProfileCheck) {
638824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // The user's profile may be returned alongside other contacts if it was requested and
638924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // the calling application has permission to read profile data.
6390377850d2dfd28eaf1b22273a50cfe066f6667ab9Dave Santoro        boolean profileRequested = readBooleanQueryParameter(uri, ContactsContract.ALLOW_PROFILE,
639124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                false);
63926ae89770d8047852b6a1f6fb3cbac812910aa476Dave Santoro        if (profileRequested && !suppressProfileCheck) {
639324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermission(false);
639424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
639524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        return profileRequested;
639624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
639724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
639843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri,
639943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            boolean includeDataSet) {
6400f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
6401f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
640243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        final String dataSet = getQueryParameter(uri, RawContacts.DATA_SET);
6403e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6404e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
6405e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
6406e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
6407fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
6408fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
6409e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
6410e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6411e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
6412e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
6413e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
6414e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
641543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            String toAppend = RawContacts.ACCOUNT_NAME + "="
64164a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
64174a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
641843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + DatabaseUtils.sqlEscapeString(accountType);
641943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            if (includeDataSet) {
642043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                if (dataSet == null) {
642143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    toAppend += " AND " + RawContacts.DATA_SET + " IS NULL";
642243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                } else {
642343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    toAppend += " AND " + RawContacts.DATA_SET + "=" +
642443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            DatabaseUtils.sqlEscapeString(dataSet);
642543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                }
642643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
642743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            qb.appendWhere(toAppend);
64284a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        } else {
64294a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere("1");
64304a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
64314a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
64324a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
6433e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    private String appendAccountToSelection(Uri uri, String selection) {
6434f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
6435f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
643643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        final String dataSet = getQueryParameter(uri, RawContacts.DATA_SET);
6437e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6438e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
6439e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
6440e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
6441fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
6442fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
6443e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
6444e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6445e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
6446e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
6447e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
6448e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
6449e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "="
6450e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
6451e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + RawContacts.ACCOUNT_TYPE + "="
6452e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountType));
645343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            if (!TextUtils.isEmpty(dataSet)) {
645443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                selectionSb.append(" AND " + RawContacts.DATA_SET + "=")
645543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        .append(DatabaseUtils.sqlEscapeString(dataSet));
645643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
6457e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (!TextUtils.isEmpty(selection)) {
6458e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(" AND (");
6459e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(selection);
6460e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(')');
6461e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
6462e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selectionSb.toString();
6463e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } else {
6464e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selection;
6465e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
6466e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    }
6467e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
64687e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
6469c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
6470c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *
6471c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
6472c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
6473c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     */
6474f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private String getLimit(Uri uri) {
64752e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limitParam = getQueryParameter(uri, ContactsContract.LIMIT_PARAM_KEY);
6476c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limitParam == null) {
6477c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
6478c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
6479c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // make sure that the limit is a non-negative integer
6480c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        try {
6481c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int l = Integer.parseInt(limitParam);
6482c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (l < 0) {
6483c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
6484c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return null;
6485c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
6486c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return String.valueOf(l);
6487c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        } catch (NumberFormatException ex) {
6488c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
6489c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
6490c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
6491c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
6492c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
6493b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    @Override
6494f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
6495415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
6496f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (mode.equals("r")) {
6497f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            waitForAccess(mReadAccessLatch);
6498f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } else {
6499f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            waitForAccess(mWriteAccessLatch);
6500f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
6501415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
6502b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        int match = sUriMatcher.match(uri);
6503b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        switch (match) {
6504a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
6505afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                SQLiteDatabase db = mDbHelper.getReadableDatabase();
650624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
6507afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForRawContact(db, rawContactId, false);
6508afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                return openPhotoAssetFile(db, uri, mode,
650924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        Data._ID + "=" + Contacts.PHOTO_ID + " AND " +
651024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                                RawContacts.CONTACT_ID + "=?",
651124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        new String[]{String.valueOf(rawContactId)});
6512e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            }
6513b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
6514f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_ID_DISPLAY_PHOTO: {
6515f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
6516f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
6517f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Display photos retrieved by contact ID can only be read.");
6518f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6519afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                SQLiteDatabase db = mDbHelper.getReadableDatabase();
6520f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long contactId = Long.parseLong(uri.getPathSegments().get(1));
6521afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
6522afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                Cursor c = db.query(Tables.CONTACTS,
6523f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{Contacts.PHOTO_FILE_ID},
6524f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        Contacts._ID + "=?", new String[]{String.valueOf(contactId)},
6525f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        null, null, null);
6526f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
6527f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.moveToFirst();
6528f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    long photoFileId = c.getLong(0);
6529f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForRead(photoFileId);
6530f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
6531f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
6532f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6533f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6534f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6535f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_DISPLAY_PHOTO:
6536f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_ID_DISPLAY_PHOTO: {
6537f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
6538f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
6539f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Display photos retrieved by contact lookup key can only be read.");
6540f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6541f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                List<String> pathSegments = uri.getPathSegments();
6542f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                int segmentCount = pathSegments.size();
6543f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (segmentCount < 4) {
6544f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
6545f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Missing a lookup key", uri));
6546f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6547afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                SQLiteDatabase db = mDbHelper.getReadableDatabase();
6548f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                String lookupKey = pathSegments.get(2);
6549f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                String[] projection = new String[]{Contacts.PHOTO_FILE_ID};
6550f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (segmentCount == 5) {
6551f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    long contactId = Long.parseLong(pathSegments.get(3));
6552afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                    enforceProfilePermissionForContact(db, contactId, false);
6553f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
6554f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
6555afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
6556f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            projection, null, null, null, null, null,
6557f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
6558f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (c != null) {
6559f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        try {
6560f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            c.moveToFirst();
6561f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            long photoFileId = c.getLong(c.getColumnIndex(Contacts.PHOTO_FILE_ID));
6562f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            return openDisplayPhotoForRead(photoFileId);
6563f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        } finally {
6564f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            c.close();
6565f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
6566f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
6567f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6568f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6569f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
6570f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                setTablesAndProjectionMapForContacts(qb, uri, projection);
6571afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                long contactId = lookupContactIdByLookupKey(db, lookupKey);
6572afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForContact(db, contactId, false);
6573afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                Cursor c = qb.query(db, projection, Contacts._ID + "=?",
6574f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{String.valueOf(contactId)}, null, null, null);
6575f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
6576f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.moveToFirst();
6577f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    long photoFileId = c.getLong(c.getColumnIndex(Contacts.PHOTO_FILE_ID));
6578f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForRead(photoFileId);
6579f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
6580f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
6581f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6582f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6583f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6584f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case RAW_CONTACTS_ID_DISPLAY_PHOTO: {
6585f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
6586f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                boolean writeable = !mode.equals("r");
6587afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                SQLiteDatabase db = mDbHelper.getReadableDatabase();
6588afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForRawContact(db, rawContactId, writeable);
6589f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6590f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Find the primary photo data record for this raw contact.
6591f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
6592f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                String[] projection = new String[]{Data._ID, Photo.PHOTO_FILE_ID};
6593f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
6594afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                Cursor c = qb.query(db, projection,
6595f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        Data.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?",
6596f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{String.valueOf(rawContactId), Photo.CONTENT_ITEM_TYPE},
6597f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        null, null, Data.IS_PRIMARY + " DESC");
6598f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long dataId = 0;
6599f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long photoFileId = 0;
6600f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
6601f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (c.getCount() >= 1) {
6602f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        c.moveToFirst();
6603f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        dataId = c.getLong(0);
6604f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        photoFileId = c.getLong(1);
6605f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
6606f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
6607f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
6608f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6609f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6610f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // If writeable, open a writeable file descriptor that we can monitor.
6611f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // When the caller finishes writing content, we'll process the photo and
6612f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // update the data record.
6613f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (writeable) {
6614f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForWrite(rawContactId, dataId, uri, mode);
6615f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } else {
6616f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForRead(photoFileId);
6617f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6618f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6619f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6620f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case DISPLAY_PHOTO: {
6621f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long photoFileId = ContentUris.parseId(uri);
6622f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
6623f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
6624f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Display photos retrieved by key can only be read.");
6625f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6626f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                return openDisplayPhotoForRead(photoFileId);
6627f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6628f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6629e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            case DATA_ID: {
6630afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                SQLiteDatabase db = mDbHelper.getReadableDatabase();
663124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long dataId = Long.parseLong(uri.getPathSegments().get(1));
6632afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                enforceProfilePermissionForData(db, dataId, false);
6633afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                return openPhotoAssetFile(db, uri, mode,
6634e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        Data._ID + "=? AND " + Data.MIMETYPE + "='" + Photo.CONTENT_ITEM_TYPE + "'",
663524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        new String[]{String.valueOf(dataId)});
6636d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
6637d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6638fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            case PROFILE_AS_VCARD: {
6639fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // When opening a contact as file, we pass back contents as a
6640fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // vCard-encoded stream. We build into a local buffer first,
6641fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // then pipe into MemoryFile once the exact size is known.
6642fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
6643fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(uri, localStream, null, null);
6644fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                return buildAssetFileDescriptor(localStream);
6645fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            }
664642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
6647fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            case CONTACTS_AS_VCARD: {
664842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // When opening a contact as file, we pass back contents as a
664942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // vCard-encoded stream. We build into a local buffer first,
665042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // then pipe into MemoryFile once the exact size is known.
665142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
6652fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(uri, localStream, null, null);
6653f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
665442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
665542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
665642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
665749d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                SQLiteDatabase db = mDbHelper.getReadableDatabase();
665842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKeys = uri.getPathSegments().get(2);
665942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String[] loopupKeyList = lookupKeys.split(":");
666042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final StringBuilder inBuilder = new StringBuilder();
6661fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                Uri queryUri = Contacts.CONTENT_URI;
666242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                int index = 0;
6663fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen
6664d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // SQLite has limits on how many parameters can be used
6665d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // so the IDs are concatenated to a query string here instead
666642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                for (String lookupKey : loopupKeyList) {
666742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    if (index == 0) {
6668d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append("(");
666942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    } else {
6670d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append(",");
667142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    }
667224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    long contactId = lookupContactIdByLookupKey(db, lookupKey);
6673afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro                    enforceProfilePermissionForContact(db, contactId, false);
667424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    inBuilder.append(contactId);
6675fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                    if (mProfileIdCache.profileContactId == contactId) {
6676fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                        queryUri = queryUri.buildUpon().appendQueryParameter(
6677377850d2dfd28eaf1b22273a50cfe066f6667ab9Dave Santoro                                ContactsContract.ALLOW_PROFILE, "true").build();
6678fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                    }
667942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    index++;
668042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                }
668142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                inBuilder.append(')');
668242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + " IN " + inBuilder.toString();
6683d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6684d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // When opening a contact as file, we pass back contents as a
6685d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // vCard-encoded stream. We build into a local buffer first,
6686d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // then pipe into MemoryFile once the exact size is known.
6687d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
6688fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(queryUri, localStream, selection, null);
6689f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
6690d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
6691b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
6692b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            default:
6693fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new FileNotFoundException(mDbHelper.exceptionMessage("File does not exist",
6694fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        uri));
6695b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        }
6696b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
6697b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
6698afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro    private AssetFileDescriptor openPhotoAssetFile(SQLiteDatabase db, Uri uri, String mode,
6699afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro            String selection, String[] selectionArgs)
6700e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throws FileNotFoundException {
6701e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        if (!"r".equals(mode)) {
6702e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throw new FileNotFoundException(mDbHelper.exceptionMessage("Mode " + mode
6703e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                    + " not supported.", uri));
6704e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        }
6705e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
6706e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        String sql =
6707ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                "SELECT " + Photo.PHOTO + " FROM " + Views.DATA +
6708e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                " WHERE " + selection;
670908ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        try {
6710f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
6711f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    DatabaseUtils.blobFileDescriptorForQuery(db, sql, selectionArgs));
671208ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        } catch (SQLiteDoneException e) {
671308ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            // this will happen if the DB query returns no rows (i.e. contact does not exist)
671408ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            throw new FileNotFoundException(uri.toString());
671508ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        }
6716e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov    }
6717e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
6718f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
6719f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Opens a display photo from the photo store for reading.
6720f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param photoFileId The display photo file ID
6721f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @return An asset file descriptor that allows the file to be read.
6722f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @throws FileNotFoundException If no photo file for the given ID exists.
6723f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
6724f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private AssetFileDescriptor openDisplayPhotoForRead(long photoFileId)
6725f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            throws FileNotFoundException {
6726f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        PhotoStore.Entry entry = mPhotoStore.get(photoFileId);
6727f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (entry != null) {
6728f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            return makeAssetFileDescriptor(
6729f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    ParcelFileDescriptor.open(new File(entry.path),
6730f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            ParcelFileDescriptor.MODE_READ_ONLY),
6731f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    entry.size);
6732f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } else {
6733f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
6734f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            throw new FileNotFoundException("No photo file found for ID " + photoFileId);
6735f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
6736f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
6737f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6738f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
6739f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Opens a file descriptor for a photo to be written.  When the caller completes writing
6740f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * to the file (closing the output stream), the image will be parsed out and processed.
6741f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * If processing succeeds, the given raw contact ID's primary photo record will be
6742f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * populated with the inserted image (if no primary photo record exists, the data ID can
6743f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * be left as 0, and a new data record will be inserted).
6744f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param rawContactId Raw contact ID this photo entry should be associated with.
6745f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param dataId Data ID for a photo mimetype that will be updated with the inserted
6746f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     image.  May be set to 0, in which case the inserted image will trigger creation
6747f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     of a new primary photo image data row for the raw contact.
6748f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param uri The URI being used to access this file.
6749f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param mode Read/write mode string.
6750f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @return An asset file descriptor the caller can use to write an image file for the
6751f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     raw contact.
6752f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
6753f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private AssetFileDescriptor openDisplayPhotoForWrite(long rawContactId, long dataId, Uri uri,
6754f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            String mode) {
6755f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        try {
6756c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            ParcelFileDescriptor[] pipeFds = ParcelFileDescriptor.createPipe();
6757c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            PipeMonitor pipeMonitor = new PipeMonitor(rawContactId, dataId, pipeFds[0]);
6758c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            pipeMonitor.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Object[]) null);
6759c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            return new AssetFileDescriptor(pipeFds[1], 0, AssetFileDescriptor.UNKNOWN_LENGTH);
6760f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } catch (IOException ioe) {
6761f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            Log.e(TAG, "Could not create temp image file in mode " + mode);
6762f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            return null;
6763f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
6764f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
6765f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6766f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
6767c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro     * Async task that monitors the given file descriptor (the read end of a pipe) for
6768c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro     * the writer finishing.  If the data from the pipe contains a valid image, the image
6769c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro     * is either inserted into the given raw contact or updated in the given data row.
6770f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
6771c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro    private class PipeMonitor extends AsyncTask<Object, Object, Object> {
6772c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro        private final ParcelFileDescriptor mDescriptor;
6773f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        private final long mRawContactId;
6774f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        private final long mDataId;
6775c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro        private PipeMonitor(long rawContactId, long dataId, ParcelFileDescriptor descriptor) {
6776f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            mRawContactId = rawContactId;
6777f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            mDataId = dataId;
6778c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            mDescriptor = descriptor;
6779f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
6780f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6781f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        @Override
6782c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro        protected Object doInBackground(Object... params) {
6783c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            AutoCloseInputStream is = new AutoCloseInputStream(mDescriptor);
6784f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            try {
6785c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                Bitmap b = BitmapFactory.decodeStream(is);
6786f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (b != null) {
6787f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    PhotoProcessor processor = new PhotoProcessor(b, mMaxDisplayPhotoDim,
6788f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            mMaxThumbnailPhotoDim);
6789f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6790f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    // Store the compressed photo in the photo store.
6791f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    long photoFileId = mPhotoStore.insert(processor);
6792f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6793c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                    // Depending on whether we already had a data row to attach the photo
6794c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                    // to, do an update or insert.
6795f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (mDataId != 0) {
6796f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Update the data record with the new photo.
6797f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        ContentValues updateValues = new ContentValues();
6798f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6799f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Signal that photo processing has already been handled.
6800f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        updateValues.put(DataRowHandlerForPhoto.SKIP_PROCESSING_KEY, true);
6801f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6802f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        if (photoFileId != 0) {
6803f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            updateValues.put(Photo.PHOTO_FILE_ID, photoFileId);
6804f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
6805f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        updateValues.put(Photo.PHOTO, processor.getThumbnailPhotoBytes());
6806c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                        update(ContentUris.withAppendedId(Data.CONTENT_URI, mDataId),
6807c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                                updateValues, null, null);
6808f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    } else {
6809f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Insert a new primary data record with the photo.
6810f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        ContentValues insertValues = new ContentValues();
6811f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6812f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Signal that photo processing has already been handled.
6813f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(DataRowHandlerForPhoto.SKIP_PROCESSING_KEY, true);
6814f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6815f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
6816f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Data.IS_PRIMARY, 1);
6817f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        if (photoFileId != 0) {
6818f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            insertValues.put(Photo.PHOTO_FILE_ID, photoFileId);
6819f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
6820f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Photo.PHOTO, processor.getThumbnailPhotoBytes());
6821f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insert(RawContacts.CONTENT_URI.buildUpon()
6822f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                .appendPath(String.valueOf(mRawContactId))
6823f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                .appendPath(RawContacts.Data.CONTENT_DIRECTORY).build(),
6824f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                insertValues);
6825f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
6826c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro
6827f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6828c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            } catch (IOException e) {
6829c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                throw new RuntimeException(e);
6830f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6831c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            return null;
6832f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
6833f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
6834f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6835d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String CONTACT_MEMORY_FILE_NAME = "contactAssetFile";
6836d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6837d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
6838f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert     * Returns an {@link AssetFileDescriptor} backed by the
6839d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * contents of the given {@link ByteArrayOutputStream}.
6840d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
6841f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor buildAssetFileDescriptor(ByteArrayOutputStream stream) {
6842d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        try {
6843d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            stream.flush();
6844d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6845d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final byte[] byteData = stream.toByteArray();
6846d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6847f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
6848f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    ParcelFileDescriptor.fromData(byteData, CONTACT_MEMORY_FILE_NAME),
6849f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    byteData.length);
6850d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        } catch (IOException e) {
6851ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            Log.w(TAG, "Problem writing stream into an ParcelFileDescriptor: " + e.toString());
6852ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            return null;
6853d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
6854d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
6855d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6856f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd) {
6857f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return makeAssetFileDescriptor(fd, AssetFileDescriptor.UNKNOWN_LENGTH);
6858f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
6859f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
6860f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd, long length) {
6861f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return fd != null ? new AssetFileDescriptor(fd, 0, length) : null;
6862f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
6863f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
6864d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
6865d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Output {@link RawContacts} matching the requested selection in the vCard
6866d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * format to the given {@link OutputStream}. This method returns silently if
6867d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * any errors encountered.
6868d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
6869fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen    private void outputRawContactsAsVCard(Uri uri, OutputStream stream,
6870fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            String selection, String[] selectionArgs) {
6871d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final Context context = this.getContext();
6872dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen        int vcardconfig = VCardConfig.VCARD_TYPE_DEFAULT;
6873fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen        if(uri.getBooleanQueryParameter(
6874fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                Contacts.QUERY_PARAMETER_VCARD_NO_PHOTO, false)) {
6875dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen            vcardconfig |= VCardConfig.FLAG_REFRAIN_IMAGE_EXPORT;
6876dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen        }
68777a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        final VCardComposer composer =
6878dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen                new VCardComposer(context, vcardconfig, false);
6879108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        Writer writer = null;
6880108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        try {
6881108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            writer = new BufferedWriter(new OutputStreamWriter(stream));
6882fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            if (!composer.init(uri, selection, selectionArgs, null)) {
6883108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                Log.w(TAG, "Failed to init VCardComposer");
6884108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                return;
6885108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            }
6886d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6887108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            while (!composer.isAfterLast()) {
6888108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                writer.write(composer.createOneEntry());
6889108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            }
6890108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        } catch (IOException e) {
6891108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            Log.e(TAG, "IOException: " + e);
6892108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        } finally {
6893108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            composer.terminate();
6894108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            if (writer != null) {
6895108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                try {
6896108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                    writer.close();
6897108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                } catch (IOException e) {
6898108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                    Log.w(TAG, "IOException during closing output stream: " + e);
6899108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                }
6900d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
6901d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
6902d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
6903b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
69044f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
69054f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
6906415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
6907415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov        waitForAccess(mReadAccessLatch);
6908415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
6909a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
69104f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
6911b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS:
6912be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return Contacts.CONTENT_TYPE;
69132d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill            case CONTACTS_LOOKUP:
6914b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_ID:
6915b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
691624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE:
6917b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_ITEM_TYPE;
6918f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD:
691942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD:
692024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD:
6921f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                return Contacts.CONTENT_VCARD_TYPE;
6922f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            case CONTACTS_ID_PHOTO:
6923f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_ID_DISPLAY_PHOTO:
6924f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_DISPLAY_PHOTO:
6925f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_ID_DISPLAY_PHOTO:
6926f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case RAW_CONTACTS_ID_DISPLAY_PHOTO:
6927f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case DISPLAY_PHOTO:
6928f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                return "image/jpeg";
6929b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS:
693024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS:
6931be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return RawContacts.CONTENT_TYPE;
6932b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS_ID:
693324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID:
6934b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return RawContacts.CONTENT_ITEM_TYPE;
6935f481f22a9323fe338672f99b88b26c5f0725cd42David Brown            case DATA:
693624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA:
6937f481f22a9323fe338672f99b88b26c5f0725cd42David Brown                return Data.CONTENT_TYPE;
6938508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
6939b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getDataMimeType(ContentUris.parseId(uri));
694048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES:
694148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_TYPE;
694248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
694348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_ITEM_TYPE;
69449005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov            case PHONE_LOOKUP:
69459005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov                return PhoneLookup.CONTENT_TYPE;
694648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS:
694748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_TYPE;
694848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
694948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_ITEM_TYPE;
695048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS:
695148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_TYPE;
695248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID:
695348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_ITEM_TYPE;
6954b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
6955b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_TYPE;
6956b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
6957b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_ITEM_TYPE;
6958b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case SETTINGS:
6959b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Settings.CONTENT_TYPE;
6960b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
6961b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_TYPE;
6962c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS:
6963c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SUGGEST_MIME_TYPE;
6964c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT:
6965c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
6966d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES:
6967d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_TYPE;
6968d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID:
6969d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_ITEM_TYPE;
6970af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS:
6971af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                return StreamItems.CONTENT_TYPE;
6972af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS_ID:
6973af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                return StreamItems.CONTENT_ITEM_TYPE;
6974af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS_ID_PHOTOS:
6975af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                return StreamItems.StreamItemPhotos.CONTENT_TYPE;
6976af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS_ID_PHOTOS_ID:
6977af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                return StreamItems.StreamItemPhotos.CONTENT_ITEM_TYPE;
6978af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS_PHOTOS:
6979af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                throw new UnsupportedOperationException("Not supported for write-only URI " + uri);
698061efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov            default:
698161efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov                return mLegacyApiSupport.getType(uri);
69824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
69834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
69847e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
698509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    public String[] getDefaultProjection(Uri uri) {
698609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        final int match = sUriMatcher.match(uri);
698709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        switch (match) {
698809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS:
698909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP:
699009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_ID:
699109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
699209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
699324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE:
699409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsProjectionMap.getColumnNames();
699509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
69968727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov            case CONTACTS_ID_ENTITIES:
699724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_ENTITIES:
69988727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov                return sEntityProjectionMap.getColumnNames();
69998727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov
700009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_VCARD:
700109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_MULTI_VCARD:
700224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD:
700309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsVCardProjectionMap.getColumnNames();
700409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
700509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS:
700609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS_ID:
700724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS:
700824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID:
700909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sRawContactsProjectionMap.getColumnNames();
701009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
701109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DATA_ID:
701209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES:
701309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES_ID:
701409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS:
701509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS_ID:
701609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS:
701709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS_ID:
701824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA:
701909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDataProjectionMap.getColumnNames();
702009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
702109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONE_LOOKUP:
702209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sPhoneLookupProjectionMap.getColumnNames();
702309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
702409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
702509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
702609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sAggregationExceptionsProjectionMap.getColumnNames();
702709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
702809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case SETTINGS:
702909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sSettingsProjectionMap.getColumnNames();
703009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
703109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES:
703209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES_ID:
703309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDirectoryProjectionMap.getColumnNames();
703409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
703509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            default:
703609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return null;
703709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
703809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    }
703909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
7040f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private class StructuredNameLookupBuilder extends NameLookupBuilder {
7041f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
7042f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public StructuredNameLookupBuilder(NameSplitter splitter) {
7043f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            super(splitter);
7044f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
7045f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
7046f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
7047f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
7048f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name) {
704978fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov            mDbHelper.insertNameLookup(rawContactId, dataId, lookupType, name);
7050f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
7051f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
7052f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
7053f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected String[] getCommonNicknameClusters(String normalizedName) {
7054d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov            return mCommonNicknameCache.getCommonNicknameClusters(normalizedName);
7055f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
7056f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
7057f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
70582d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov    public void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
7059d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
7060d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + RawContacts.CONTACT_ID +
7061d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.RAW_CONTACTS +
7062d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " JOIN " + Tables.NAME_LOOKUP +
7063d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " ON(" + RawContactsColumns.CONCRETE_ID + "="
7064d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
7065d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE normalized_name GLOB '");
7066e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
7067916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE +
7068916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " IN(" + CONTACT_LOOKUP_NAME_TYPES + "))");
7069e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    }
7070e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
70719a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    public boolean isPhoneNumber(String filter) {
70729a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        boolean atLeastOneDigit = false;
70739a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        int len = filter.length();
70749a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        for (int i = 0; i < len; i++) {
70759a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            char c = filter.charAt(i);
70769a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            if (c >= '0' && c <= '9') {
70779a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                atLeastOneDigit = true;
70789a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            } else if (c != '*' && c != '#' && c != '+' && c != 'N' && c != '.' && c != ';'
70799a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                    && c != '-' && c != '(' && c != ')' && c != ' ') {
70809a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                return false;
70819a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            }
70829a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
70839a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return atLeastOneDigit;
70849a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
70859a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
70864a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /**
70877a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * Takes components of a name from the query parameters and returns a cursor with those
70887a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * components as well as all missing components.  There is no database activity involved
70897a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * in this so the call can be made on the UI thread.
70907a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     */
70917a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private Cursor completeName(Uri uri, String[] projection) {
70927a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        if (projection == null) {
70937a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            projection = sDataProjectionMap.getColumnNames();
70947a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
70957a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
70967a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        ContentValues values = new ContentValues();
7097f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        DataRowHandlerForStructuredName handler = (DataRowHandlerForStructuredName)
7098f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                getDataRowHandler(StructuredName.CONTENT_ITEM_TYPE);
70997a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
71007a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        copyQueryParamsToContentValues(values, uri,
71017a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.DISPLAY_NAME,
71027a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PREFIX,
71037a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.GIVEN_NAME,
71047a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.MIDDLE_NAME,
71057a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.FAMILY_NAME,
71067a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.SUFFIX,
71077a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_NAME,
71087a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_FAMILY_NAME,
71097a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_MIDDLE_NAME,
71107a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_GIVEN_NAME
71117a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        );
71127a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
71137a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        handler.fixStructuredNameComponents(values, values);
71147a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
71157a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
71167a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        Object[] row = new Object[projection.length];
71177a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
71187a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            row[i] = values.get(projection[i]);
71197a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
71207a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        cursor.addRow(row);
71217a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        return cursor;
71227a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
71237a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
71247a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private void copyQueryParamsToContentValues(ContentValues values, Uri uri, String... columns) {
71257a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (String column : columns) {
71267a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            String param = uri.getQueryParameter(column);
71277a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            if (param != null) {
71287a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                values.put(column, param);
71297a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
71307a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
71317a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
71327a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
71337a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
71347a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    /**
71354a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     * Inserts an argument at the beginning of the selection arg list.
71364a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     */
71374a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private String[] insertSelectionArg(String[] selectionArgs, String arg) {
7138b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
7139b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
7140b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
7141b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
7142b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
71434a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            newSelectionArgs[0] = arg;
71444a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length);
7145b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
7146b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
7147b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
7148caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
71495e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private String[] appendProjectionArg(String[] projection, String arg) {
71505e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        if (projection == null) {
71515e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            return null;
71525e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        }
71535e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        final int length = projection.length;
71545e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        String[] newProjection = new String[length + 1];
71555e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        System.arraycopy(projection, 0, newProjection, 0, length);
71565e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        newProjection[length] = arg;
71575e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        return newProjection;
71585e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    }
71595e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
7160caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    protected Account getDefaultAccount() {
7161caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        AccountManager accountManager = AccountManager.get(getContext());
7162caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        try {
71635f1f4a062ac34d75d2dbf586702cbeb121cf09caDmitri Plotnikov            Account[] accounts = accountManager.getAccountsByType(DEFAULT_ACCOUNT_TYPE);
7164caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            if (accounts != null && accounts.length > 0) {
7165caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov                return accounts[0];
7166caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            }
7167caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        } catch (Throwable e) {
71686f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov            Log.e(TAG, "Cannot determine the default account for contacts compatibility", e);
7169caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        }
71706f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov        return null;
7171caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    }
7172f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
717373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    /**
717443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * Returns true if the specified account type and data set is writable.
717573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     */
717643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    protected boolean isWritableAccountWithDataSet(String accountTypeAndDataSet) {
717743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        if (accountTypeAndDataSet == null) {
7178bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov            return true;
7179bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        }
7180bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov
718143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        Boolean writable = mAccountWritability.get(accountTypeAndDataSet);
718273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable != null) {
718373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            return writable;
718473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
718573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
7186627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        IContentService contentService = ContentResolver.getContentService();
7187627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
718843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // TODO(dsantoro): Need to update this logic to allow for sub-accounts.
7189627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (SyncAdapterType sync : contentService.getSyncAdapterTypes()) {
7190627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (ContactsContract.AUTHORITY.equals(sync.authority) &&
719143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        accountTypeAndDataSet.equals(sync.accountType)) {
719273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    writable = sync.supportsUploading();
719373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    break;
7194627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
7195627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
7196627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } catch (RemoteException e) {
7197627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            Log.e(TAG, "Could not acquire sync adapter types");
7198627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
719973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
720073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable == null) {
720173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            writable = false;
720273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
720373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
720443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        mAccountWritability.put(accountTypeAndDataSet, writable);
720573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        return writable;
7206627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
7207b4e61e064b59e8076df81b061add9fb358fd2ed9Dmitri Plotnikov
7208d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
7209f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static boolean readBooleanQueryParameter(Uri uri, String parameter,
7210f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean defaultValue) {
7211f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7212f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        // Manually parse the query, which is much faster than calling uri.getQueryParameter
7213f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
7214f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
7215f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
7216f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7217f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7218f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = query.indexOf(parameter);
7219f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (index == -1) {
7220f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
7221f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7222f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7223f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        index += parameter.length();
7224f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7225f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return !matchQueryParameter(query, index, "=0", false)
7226f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && !matchQueryParameter(query, index, "=false", true);
7227f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
7228f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7229f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private static boolean matchQueryParameter(String query, int index, String value,
7230f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean ignoreCase) {
7231f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int length = value.length();
7232f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return query.regionMatches(ignoreCase, index, value, 0, length)
7233f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && (query.length() == index + length || query.charAt(index + length) == '&');
7234f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
7235f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7236f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /**
7237f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * A fast re-implementation of {@link Uri#getQueryParameter}
7238f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     */
7239f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static String getQueryParameter(Uri uri, String parameter) {
7240f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
7241f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
7242f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return null;
7243f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7244f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7245f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int queryLength = query.length();
7246f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int parameterLength = parameter.length();
7247f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7248f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String value;
7249f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = 0;
7250f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        while (true) {
7251f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index = query.indexOf(parameter, index);
7252f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (index == -1) {
7253f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
72545fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            }
72555fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa
72565fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            // Should match against the whole parameter instead of its suffix.
72575fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            // e.g. The parameter "param" must not be found in "some_param=val".
72585fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            if (index > 0) {
72595fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                char prevChar = query.charAt(index - 1);
72605fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                if (prevChar != '?' && prevChar != '&') {
72615fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    // With "some_param=val1&param=val2", we should find second "param" occurrence.
72625fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    index += parameterLength;
72635fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    continue;
72645fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                }
7265f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
7266f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7267f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index += parameterLength;
7268f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7269f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (queryLength == index) {
7270f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
7271f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
7272f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7273f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (query.charAt(index) == '=') {
7274f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                index++;
7275f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                break;
7276f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
7277f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7278f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7279f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int ampIndex = query.indexOf('&', index);
7280f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (ampIndex == -1) {
7281f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index);
7282f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        } else {
7283f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index, ampIndex);
7284f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7285f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7286f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return Uri.decode(value);
7287f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
72885dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
72890dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    protected boolean isAggregationUpgradeNeeded() {
72900dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
72910dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            return false;
72920dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
72930dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
72940dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int version = Integer.parseInt(mDbHelper.getProperty(PROPERTY_AGGREGATION_ALGORITHM, "1"));
72950dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        return version < PROPERTY_AGGREGATION_ALGORITHM_VERSION;
72960dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
72970dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
7298bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void upgradeAggregationAlgorithmInBackground() {
72990dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // This upgrade will affect very few contacts, so it can be performed on the
73000dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // main thread during the initial boot after an OTA
73010dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
73020dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        Log.i(TAG, "Upgrading aggregation algorithm");
73030dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int count = 0;
73040dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        long start = SystemClock.currentThreadTimeMillis();
73050dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        try {
730649d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov            mDb = mDbHelper.getWritableDatabase();
73070dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.beginTransaction();
73080dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            Cursor cursor = mDb.query(true,
73090dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    Tables.RAW_CONTACTS + " r1 JOIN " + Tables.RAW_CONTACTS + " r2",
73100dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    new String[]{"r1." + RawContacts._ID},
73110dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    "r1." + RawContacts._ID + "!=r2." + RawContacts._ID +
73120dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.CONTACT_ID + "=r2." + RawContacts.CONTACT_ID +
73130dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.ACCOUNT_NAME + "=r2." + RawContacts.ACCOUNT_NAME +
731443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    " AND r1." + RawContacts.ACCOUNT_TYPE + "=r2." + RawContacts.ACCOUNT_TYPE +
731543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    " AND r1." + RawContacts.DATA_SET + "=r2." + RawContacts.DATA_SET,
73160dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    null, null, null, null, null);
73170dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            try {
73180dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                while (cursor.moveToNext()) {
73190dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    long rawContactId = cursor.getLong(0);
73200dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId,
73210dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                            RawContacts.AGGREGATION_MODE_DEFAULT, true);
73220dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    count++;
73230dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                }
73240dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            } finally {
73250dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                cursor.close();
73260dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            }
7327bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mContactAggregator.aggregateInTransaction(mTransactionContext, mDb);
7328bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            updateSearchIndexInTransaction();
73290dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.setTransactionSuccessful();
73300dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDbHelper.setProperty(PROPERTY_AGGREGATION_ALGORITHM,
73310dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    String.valueOf(PROPERTY_AGGREGATION_ALGORITHM_VERSION));
73320dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        } finally {
73330dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.endTransaction();
73340dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            long end = SystemClock.currentThreadTimeMillis();
73350dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            Log.i(TAG, "Aggregation algorithm upgraded for " + count
73360dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    + " contacts, in " + (end - start) + "ms");
73370dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
73380dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
73399a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
73409a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    /* Visible for testing */
73419a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    boolean isPhone() {
73429a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (!sIsPhoneInitialized) {
73439a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sIsPhone = new TelephonyManager(getContext()).isVoiceCapable();
73449a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sIsPhoneInitialized = true;
73459a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
73469a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return sIsPhone;
73479a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
734846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
734946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private boolean handleDataUsageFeedback(Uri uri) {
735046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final long currentTimeMillis = System.currentTimeMillis();
735146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String usageType = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
735246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] ids = uri.getLastPathSegment().trim().split(",");
735346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final ArrayList<Long> dataIds = new ArrayList<Long>();
735446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
735546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        for (String id : ids) {
735646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            dataIds.add(Long.valueOf(id));
735746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
735846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final boolean successful;
735946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (TextUtils.isEmpty(usageType)) {
736046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            Log.w(TAG, "Method for data usage feedback isn't specified. Ignoring.");
736146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            successful = false;
736246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } else {
736346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            successful = updateDataUsageStat(dataIds, usageType, currentTimeMillis) > 0;
736446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
736546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
736646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        // Handle old API. This doesn't affect the result of this entire method.
736746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] questionMarks = new String[ids.length];
736846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        Arrays.fill(questionMarks, "?");
736946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String where = Data._ID + " IN (" + TextUtils.join(",", questionMarks) + ")";
737046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final Cursor cursor = mDb.query(
7371ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                Views.DATA,
737246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                new String[] { Data.CONTACT_ID },
737346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                where, ids, null, null, null);
737446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        try {
737546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            while (cursor.moveToNext()) {
737646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mSelectionArgs1[0] = cursor.getString(0);
737746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                ContentValues values2 = new ContentValues();
737846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                values2.put(Contacts.LAST_TIME_CONTACTED, currentTimeMillis);
737946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDb.update(Tables.CONTACTS, values2, Contacts._ID + "=?", mSelectionArgs1);
738046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDb.execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
738146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDb.execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
738246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
738346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } finally {
738446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            cursor.close();
738546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
738646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
738746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        return successful;
738846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
738946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
739046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
739146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Update {@link Tables#DATA_USAGE_STAT}.
739246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     *
739346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * @return the number of rows affected.
739446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
7395f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa    @VisibleForTesting
7396f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa    /* package */ int updateDataUsageStat(
7397f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa            List<Long> dataIds, String type, long currentTimeMillis) {
739846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final int typeInt = sDataUsageTypeMap.get(type);
739946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String where = DataUsageStatColumns.DATA_ID + " =? AND "
740046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                + DataUsageStatColumns.USAGE_TYPE_INT + " =?";
740146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] columns =
740246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                new String[] { DataUsageStatColumns._ID, DataUsageStatColumns.TIMES_USED };
740346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final ContentValues values = new ContentValues();
740446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        for (Long dataId : dataIds) {
740546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            final String[] args = new String[] { dataId.toString(), String.valueOf(typeInt) };
740646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            mDb.beginTransaction();
740746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            try {
740846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                final Cursor cursor = mDb.query(Tables.DATA_USAGE_STAT, columns, where, args,
740946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        null, null, null);
741046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                try {
741146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (cursor.getCount() > 0) {
741246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        if (!cursor.moveToFirst()) {
741346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            Log.e(TAG,
741446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    "moveToFirst() failed while getAccount() returned non-zero.");
741546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        } else {
741646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.clear();
741746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.put(DataUsageStatColumns.TIMES_USED, cursor.getInt(1) + 1);
741846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.put(DataUsageStatColumns.LAST_TIME_USED, currentTimeMillis);
741946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            mDb.update(Tables.DATA_USAGE_STAT, values,
742046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    DataUsageStatColumns._ID + " =?",
742146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    new String[] { cursor.getString(0) });
742246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        }
742346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    } else {
742446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.clear();
742546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.DATA_ID, dataId);
742646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.USAGE_TYPE_INT, typeInt);
742746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.TIMES_USED, 1);
742846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.LAST_TIME_USED, currentTimeMillis);
742946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        mDb.insert(Tables.DATA_USAGE_STAT, null, values);
743046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    }
743146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    mDb.setTransactionSuccessful();
743246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                } finally {
743346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    cursor.close();
743446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
743546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            } finally {
743646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDb.endTransaction();
743746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
743846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
743946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
744046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        return dataIds.size();
744146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
744246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
744346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
744446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Returns a sort order String for promoting data rows (email addresses, phone numbers, etc.)
744546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * associated with a primary account. The primary account should be supplied from applications
744646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * with {@link ContactsContract#PRIMARY_ACCOUNT_NAME} and
744746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * {@link ContactsContract#PRIMARY_ACCOUNT_TYPE}. Null will be returned when the primary
744846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * account isn't available.
744946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
745046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private String getAccountPromotionSortOrder(Uri uri) {
745146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String primaryAccountName =
745246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME);
745346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String primaryAccountType =
745446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE);
745546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
745646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        // Data rows associated with primary account should be promoted.
745746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (!TextUtils.isEmpty(primaryAccountName)) {
745846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            StringBuilder sb = new StringBuilder();
745946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            sb.append("(CASE WHEN " + RawContacts.ACCOUNT_NAME + "=");
746046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            DatabaseUtils.appendEscapedSQLString(sb, primaryAccountName);
746146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            if (!TextUtils.isEmpty(primaryAccountType)) {
746246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                sb.append(" AND " + RawContacts.ACCOUNT_TYPE + "=");
746346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DatabaseUtils.appendEscapedSQLString(sb, primaryAccountType);
746446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
746546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            sb.append(" THEN 0 ELSE 1 END)");
746646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            return sb.toString();
746746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } else {
746846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            return null;
746946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
747046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
74714f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
7472