ContactsProvider2.java revision 3711af1a5799a7ae0c8e761e13a67a9fb5878cc8
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;
765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoroimport android.content.pm.ProviderInfo;
77f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringertimport android.content.res.AssetFileDescriptor;
783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmannimport android.content.res.Resources;
790bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmannimport android.content.res.Resources.NotFoundException;
80409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onukiimport android.database.AbstractCursor;
81e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikovimport android.database.CrossProcessCursor;
824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.Cursor;
83e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikovimport android.database.CursorWindow;
84ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.database.CursorWrapper;
85ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.database.DatabaseUtils;
8609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor;
8709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor.RowBuilder;
88f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoroimport android.database.sqlite.SQLiteConstraintException;
894f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteDatabase;
9008ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwoodimport android.database.sqlite.SQLiteDoneException;
915d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoroimport android.database.sqlite.SQLiteOpenHelper;
924f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteQueryBuilder;
93f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.graphics.Bitmap;
94f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.graphics.BitmapFactory;
954f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.net.Uri;
96d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.net.Uri.Builder;
97c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoroimport android.os.AsyncTask;
98bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Binder;
996ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.os.Bundle;
100bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Handler;
101bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.HandlerThread;
102bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Message;
103ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringertimport android.os.ParcelFileDescriptor;
104c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoroimport android.os.ParcelFileDescriptor.AutoCloseInputStream;
105bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Process;
106b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.os.RemoteException;
10715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikovimport android.os.StrictMode;
1080dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikovimport android.os.SystemClock;
1090e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport android.os.SystemProperties;
1103d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.preference.PreferenceManager;
111508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkeyimport android.provider.BaseColumns;
1123de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract;
113b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions;
11497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Email;
11597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
11697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Im;
11797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Nickname;
1186d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Note;
11997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Organization;
12097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Phone;
12197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Photo;
1224928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.SipAddress;
12397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
12497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
125ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.provider.ContactsContract.ContactCounts;
1263de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Contacts;
1275b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikovimport android.provider.ContactsContract.Contacts.AggregationSuggestions;
1283de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Data;
12971340347b4862d4b1368a5d69d1667e2245952e4Daisuke Miyakawaimport android.provider.ContactsContract.DataUsageFeedback;
130d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.provider.ContactsContract.Directory;
131f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.provider.ContactsContract.DisplayPhoto;
1323de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Groups;
133bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.provider.ContactsContract.Intents;
1343de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.PhoneLookup;
1351dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoroimport android.provider.ContactsContract.PhotoFiles;
1360c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoroimport android.provider.ContactsContract.Profile;
13709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.provider.ContactsContract.ProviderStatus;
1383de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.RawContacts;
1393711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenenimport android.provider.ContactsContract.RawContactsEntity;
140916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikovimport android.provider.ContactsContract.SearchSnippetColumns;
1413de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Settings;
14282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikovimport android.provider.ContactsContract.StatusUpdates;
1433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmannimport android.provider.ContactsContract.StreamItemPhotos;
144f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.provider.ContactsContract.StreamItems;
14597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.LiveFolders;
14697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.OpenableColumns;
14797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.SyncStateContract;
148a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.telephony.PhoneNumberUtils;
1499a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikovimport android.telephony.TelephonyManager;
150a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.text.TextUtils;
151c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.util.Log;
1524f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
153108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawaimport java.io.BufferedWriter;
154d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.ByteArrayOutputStream;
155f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport java.io.File;
156b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport java.io.FileNotFoundException;
157d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.IOException;
158d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.OutputStream;
159108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawaimport java.io.OutputStreamWriter;
160108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawaimport java.io.Writer;
16142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.text.SimpleDateFormat;
1627e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport java.util.ArrayList;
16346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawaimport java.util.Arrays;
1645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.Collections;
16542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.util.Date;
166b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport java.util.HashMap;
1670e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.HashSet;
1685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.List;
169622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkeyimport java.util.Locale;
170b5a4add17815167d20a90645779df34cdf45280dFred Quintanaimport java.util.Map;
1710e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.Set;
172ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikovimport java.util.concurrent.CountDownLatch;
1734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1744f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/**
1754f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Contacts content provider. The contract between this provider and applications
1764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * is defined in {@link ContactsContract}.
1774f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
1785b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanapublic class ContactsProvider2 extends SQLiteContentProvider implements OnAccountsUpdateListener {
179caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
180bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final String TAG = "ContactsProvider";
181bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov
182bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE);
1834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
18415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_INITIALIZE = 0;
18515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_OPEN_WRITE_ACCESS = 1;
18615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS = 2;
18715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_ACCOUNTS = 3;
18815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_LOCALE = 4;
18915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM = 5;
19005e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_SEARCH_INDEX = 6;
19105e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_PROVIDER_STATUS = 7;
19205e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_DIRECTORIES = 8;
19305e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_CHANGE_LOCALE = 9;
194f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int BACKGROUND_TASK_CLEANUP_PHOTOS = 10;
195619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Default for the maximum number of returned aggregation suggestions. */
1973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DEFAULT_MAX_SUGGESTIONS = 5;
1983cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /** Limit for the maximum number of social stream items to store under a raw contact. */
2003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int MAX_STREAM_ITEMS_PER_RAW_CONTACT = 5;
2013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
202f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /** Rate limit (in ms) for photo cleanup.  Do it at most once per day. */
203f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int PHOTO_CLEANUP_RATE_LIMIT = 24 * 60 * 60 * 1000;
204f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
2053d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /**
206b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov     * Property key for the legacy contact import version. The need for a version
2073d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * as opposed to a boolean flag is that if we discover bugs in the contact import process,
2083d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * we can trigger re-import by incrementing the import version.
2093d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     */
210b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final String PROPERTY_CONTACTS_IMPORTED = "contacts_imported_v1";
211b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final int PROPERTY_CONTACTS_IMPORT_VERSION = 1;
21251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    private static final String PREF_LOCALE = "locale";
2133d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2140dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static final String PROPERTY_AGGREGATION_ALGORITHM = "aggregation_v2";
2150dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static final int PROPERTY_AGGREGATION_ALGORITHM_VERSION = 2;
2160dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
2170e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff    private static final String AGGREGATE_CONTACTS = "sync.contacts.aggregate";
2180e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff
2195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private static final ProfileAwareUriMatcher sUriMatcher =
2205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            new ProfileAwareUriMatcher(UriMatcher.NO_MATCH);
2214f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2222f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    /**
2232f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     * Used to insert a column into strequent results, which enables SQL to sort the list using
2242f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     * the total times contacted. See also {@link #sStrequentFrequentProjectionMap}.
2252f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     */
2262f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    private static final String TIMES_USED_SORT_COLUMN = "times_used_sort";
2275e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
228d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final String STREQUENT_ORDER_BY = Contacts.STARRED + " DESC, "
2292f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            + TIMES_USED_SORT_COLUMN + " DESC, "
2309b43551f1ce33b79141772737a262ce609bd0cebMegha Joshi            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
231d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final String STREQUENT_LIMIT =
232d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            "(SELECT COUNT(1) FROM " + Tables.CONTACTS + " WHERE "
233d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            + Contacts.STARRED + "=1) + 25";
234d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
23545ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa    private static final String FREQUENT_ORDER_BY = DataUsageStatColumns.TIMES_USED + " DESC,"
23645ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
23745ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa
2386e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_CONTACTS_TABLE =
2399b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.CONTACTS + " SET " + Contacts.TIMES_CONTACTED + "=" +
2409b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + Contacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
2419b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + Contacts.TIMES_CONTACTED + " + 1) END WHERE " + Contacts._ID + "=?";
2429b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
2436e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE =
2449b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.RAW_CONTACTS + " SET " + RawContacts.TIMES_CONTACTED + "=" +
2459b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + RawContacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
2469b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + RawContacts.TIMES_CONTACTED + " + 1) END WHERE " + RawContacts.CONTACT_ID + "=?";
2479b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
248de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa    /* package */ static final String PHONEBOOK_COLLATOR_NAME = "PHONEBOOK";
249de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa
2503716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    // Regex for splitting query strings - we split on any group of non-alphanumeric characters,
2513716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    // excluding the @ symbol.
2523716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    /* package */ static final String QUERY_TOKENIZER_REGEX = "[^\\w@]+";
2533716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
254d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS = 1000;
255d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS_ID = 1001;
2565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP = 1002;
2575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID = 1003;
258a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_DATA = 1004;
2595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_FILTER = 1005;
2605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT = 1006;
2615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT_FILTER = 1007;
2625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_GROUP = 1008;
263a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_PHOTO = 1009;
264bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_PHOTO = 1010;
265bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_PHOTO = 1011;
266bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_ID_DISPLAY_PHOTO = 1012;
267bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_DISPLAY_PHOTO = 1013;
268bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_DISPLAY_PHOTO = 1014;
269bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_AS_VCARD = 1015;
270bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_AS_MULTI_VCARD = 1016;
271bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_DATA = 1017;
272bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_DATA = 1018;
273bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_ID_ENTITIES = 1019;
274bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ENTITIES = 1020;
275bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_ENTITIES = 1021;
276bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_ID_STREAM_ITEMS = 1022;
277bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_STREAM_ITEMS = 1023;
278bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_STREAM_ITEMS = 1024;
279bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_FREQUENT = 1025;
2804f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2815ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS = 2002;
2825ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_ID = 2003;
2835ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_DATA = 2004;
28446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITY_ID = 2005;
285f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int RAW_CONTACTS_ID_DISPLAY_PHOTO = 2006;
286f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int RAW_CONTACTS_ID_STREAM_ITEMS = 2007;
28782780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro    private static final int RAW_CONTACTS_ID_STREAM_ITEMS_ID = 2008;
2884f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2896bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
2906bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
291ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
29248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_ID = 3003;
29348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_FILTER = 3004;
29448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS = 3005;
29548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_ID = 3006;
29648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_LOOKUP = 3007;
29748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_FILTER = 3008;
29848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS = 3009;
29948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS_ID = 3010;
300a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
3016bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
3026bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
303b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
304b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
305b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
30682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES = 7000;
30782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES_ID = 7001;
3081f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
30931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
31031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
311eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final int SETTINGS = 9000;
312eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
313ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
314ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
315ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
316ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
31735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
318b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private static final int SYNCSTATE_ID = 11001;
3195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private static final int PROFILE_SYNCSTATE = 11002;
3205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private static final int PROFILE_SYNCSTATE_ID = 11003;
32135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
322c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SUGGESTIONS = 12001;
323c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SHORTCUT = 12002;
324c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
3251b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS = 14000;
3261b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_WITH_PHONES = 14001;
3271b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_FAVORITES = 14002;
3281b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_GROUP_NAME = 14003;
3291b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
33046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITIES = 15001;
33146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
33209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private static final int PROVIDER_STATUS = 16001;
33309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
334d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES = 17001;
335d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES_ID = 17002;
336d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3377a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private static final int COMPLETE_NAME = 18000;
3387a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
33924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE = 19000;
34024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_ENTITIES = 19001;
34124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_DATA = 19002;
34224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_DATA_ID = 19003;
34324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_AS_VCARD = 19004;
34424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS = 19005;
34524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID = 19006;
34624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID_DATA = 19007;
34724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID_ENTITIES = 19008;
3485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private static final int PROFILE_STATUS_UPDATES = 19009;
3493202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro    private static final int PROFILE_RAW_CONTACT_ENTITIES = 19010;
35024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
35146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final int DATA_USAGE_FEEDBACK_ID = 20001;
35246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
3533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS = 21000;
3543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_PHOTOS = 21001;
3553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID = 21002;
3563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID_PHOTOS = 21003;
3573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID_PHOTOS_ID = 21004;
3583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_LIMIT = 21005;
3593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
360f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int DISPLAY_PHOTO = 22000;
361f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int PHOTO_DIMENSIONS = 22001;
362f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
3635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Inserts into URIs in this map will direct to the profile database if the parent record's
3645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // value (looked up from the ContentValues object with the key specified by the value in this
3655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // map) is in the profile ID-space (see {@link ProfileDatabaseHelper#PROFILE_ID_SPACE}).
3665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private static final Map<Integer, String> INSERT_URI_ID_VALUE_MAP = Maps.newHashMap();
3675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    static {
3685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(DATA, Data.RAW_CONTACT_ID);
3695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(RAW_CONTACTS_DATA, Data.RAW_CONTACT_ID);
3705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(STATUS_UPDATES, StatusUpdates.DATA_ID);
3715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(STREAM_ITEMS, StreamItems.RAW_CONTACT_ID);
3725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(RAW_CONTACTS_ID_STREAM_ITEMS, StreamItems.RAW_CONTACT_ID);
3735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(STREAM_ITEMS_PHOTOS, StreamItemPhotos.STREAM_ITEM_ID);
3745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(STREAM_ITEMS_ID_PHOTOS, StreamItemPhotos.STREAM_ITEM_ID);
3755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
3765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
377dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID =
378dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
379dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME
380dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
381dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE
38243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AND ("
38343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + GroupsColumns.CONCRETE_DATA_SET
38443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + "=" + RawContactsColumns.CONCRETE_DATA_SET + " OR "
38543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + GroupsColumns.CONCRETE_DATA_SET + " IS NULL AND "
38643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + RawContactsColumns.CONCRETE_DATA_SET + " IS NULL)"
387dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " AND " + Groups.FAVORITES + " != 0";
388dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
389dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID =
390dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
391dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
392dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
393dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
39443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AND ("
39543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + GroupsColumns.CONCRETE_DATA_SET + "="
39643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + RawContactsColumns.CONCRETE_DATA_SET + " OR "
39743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + GroupsColumns.CONCRETE_DATA_SET + " IS NULL AND "
39843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + RawContactsColumns.CONCRETE_DATA_SET + " IS NULL)"
39943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + " AND " + Groups.AUTO_ADD + " != 0";
400dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
401dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String[] PROJECTION_GROUP_ID
402dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            = new String[]{Tables.GROUPS + "." + Groups._ID};
403dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
404dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_GROUPMEMBERSHIP_DATA = DataColumns.MIMETYPE_ID + "=? "
405dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.GROUP_ROW_ID + "=? "
406dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.RAW_CONTACT_ID + "=?";
407dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
408dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_STARRED_FROM_RAW_CONTACTS =
409dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            "SELECT " + RawContacts.STARRED
410dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " FROM " + Tables.RAW_CONTACTS + " WHERE " + RawContacts._ID + "=?";
411dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
412d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private interface DataContactsQuery {
413f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        public static final String TABLE = "data "
414f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) "
415f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN contacts ON (raw_contacts.contact_id = contacts._id)";
41667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
41767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
4186cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
4196802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            RawContactsColumns.CONCRETE_ACCOUNT_TYPE,
4206802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            RawContactsColumns.CONCRETE_ACCOUNT_NAME,
42143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            RawContactsColumns.CONCRETE_DATA_SET,
4223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
423f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            ContactsColumns.CONCRETE_ID
424ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
425ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
426d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
4276802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        public static final int ACCOUNT_TYPE = 1;
4286802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        public static final int ACCOUNT_NAME = 2;
42943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        public static final int DATA_SET = 3;
43043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        public static final int DATA_ID = 4;
43143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        public static final int CONTACT_ID = 5;
432ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
4331f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
434f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov    interface RawContactsQuery {
43519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String TABLE = Tables.RAW_CONTACTS;
43619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
43719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String[] COLUMNS = new String[] {
438ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.DELETED,
439ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
440ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
44143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.DATA_SET,
44219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        };
44319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
44419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int DELETED = 0;
445ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_TYPE = 1;
446ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_NAME = 2;
44743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int DATA_SET = 3;
44819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    }
44919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
450c76cdd0723b99f478c9ba5329d14a971cd8dfb3dCostin Manolache    public static final String DEFAULT_ACCOUNT_TYPE = "com.google";
451caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
45271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    /** Sql where statement for filtering on groups. */
45371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private static final String CONTACTS_IN_GROUP_SELECT =
45471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov            Contacts._ID + " IN "
45571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + "(SELECT " + RawContacts.CONTACT_ID
45671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " FROM " + Tables.RAW_CONTACTS
45771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + " IN "
45871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
45971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " FROM " + Tables.DATA_JOIN_MIMETYPES
4607cf50494501938f175d288077145acf49da8f171Daniel Lehmann                            + " WHERE " + DataColumns.MIMETYPE_ID + "=?"
4617cf50494501938f175d288077145acf49da8f171Daniel Lehmann                                    + " AND " + GroupMembership.GROUP_ROW_ID + "="
46271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "(SELECT " + Tables.GROUPS + "." + Groups._ID
46371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " FROM " + Tables.GROUPS
46471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " WHERE " + Groups.TITLE + "=?)))";
46571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov
466a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating DIRTY flag on multiple raw contacts */
467a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_DIRTY_SQL =
468a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
469a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.DIRTY + "=1" +
470a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
471a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
472a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating VERSION on multiple raw contacts */
473a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_VERSION_SQL =
474a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
475a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.VERSION + " = " + RawContacts.VERSION + " + 1" +
476a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
477a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
478c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Current contacts - those contacted within the last 3 days (in seconds)
479c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_CURRENT = 3 * 24 * 60 * 60;
480c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
481c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Recent contacts - those contacted within the last 30 days (in seconds)
482c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_RECENT = 30 * 24 * 60 * 60;
483c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
484f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa    private static final String TIME_SINCE_LAST_USED =
485f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa            "(strftime('%s', 'now') - " + DataUsageStatColumns.LAST_TIME_USED + "/1000)";
486f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa
487c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    /*
488c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * Sorting order for email address suggestions: first starred, then the rest.
4892262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * second in_visible_group, then the rest.
4902262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * Within the four (starred/unstarred, in_visible_group/not-in_visible_group) groups
4912262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * - three buckets: very recently contacted, then fairly
492c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * recently contacted, then the rest.  Within each of the bucket - descending count
49346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * of times contacted (both for data row and for contact row). If all else fails, alphabetical.
49446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * (Super)primary email address is returned before other addresses for the same contact.
495c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     */
496c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final String EMAIL_FILTER_SORT_ORDER =
4972262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa        Contacts.STARRED + " DESC, "
4982262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa        + Contacts.IN_VISIBLE_GROUP + " DESC, "
499f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa        + "(CASE WHEN " + TIME_SINCE_LAST_USED + " < " + EMAIL_FILTER_CURRENT
50046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " THEN 0 "
501f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa                + " WHEN " + TIME_SINCE_LAST_USED + " < " + EMAIL_FILTER_RECENT
50246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " THEN 1 "
50346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " ELSE 2 END), "
50446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + DataUsageStatColumns.TIMES_USED + " DESC, "
50546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + Contacts.DISPLAY_NAME + ", "
50646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + Data.CONTACT_ID + ", "
507c591cc2ffecdd0038f787a133606752752294c13Daisuke Miyakawa        + Data.IS_SUPER_PRIMARY + " DESC, "
508c591cc2ffecdd0038f787a133606752752294c13Daisuke Miyakawa        + Data.IS_PRIMARY + " DESC";
50946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
51046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /** Currently same as {@link #EMAIL_FILTER_SORT_ORDER} */
51146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final String PHONE_FILTER_SORT_ORDER = EMAIL_FILTER_SORT_ORDER;
512c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
513916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Name lookup types used for contact filtering */
514916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private static final String CONTACT_LOOKUP_NAME_TYPES =
515916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NAME_COLLATION_KEY + "," +
516916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.EMAIL_BASED_NICKNAME + "," +
51792ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov            NameLookupType.NICKNAME;
518916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
519f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    /**
520f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * If any of these columns are used in a Data projection, there is no point in
521f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * using the DISTINCT keyword, which can negatively affect performance.
522f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     */
523f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    private static final String[] DISTINCT_DATA_PROHIBITING_COLUMNS = {
524f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data._ID,
525f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.RAW_CONTACT_ID,
526f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.NAME_RAW_CONTACT_ID,
527f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_NAME,
528f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_TYPE,
52943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            RawContacts.DATA_SET,
53043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
531f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.DIRTY,
532f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.NAME_VERIFIED,
533f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.SOURCE_ID,
534f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.VERSION,
535f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    };
536916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
537f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsColumns = ProjectionMap.builder()
538f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CUSTOM_RINGTONE)
539f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME)
540f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_ALTERNATIVE)
541f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_SOURCE)
542f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.IN_VISIBLE_GROUP)
543f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LAST_TIME_CONTACTED)
544f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LOOKUP_KEY)
545f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME)
546f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME_STYLE)
547f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHOTO_ID)
548f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            .add(Contacts.PHOTO_FILE_ID)
5493d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_URI)
5503d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_THUMBNAIL_URI)
551f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SEND_TO_VOICEMAIL)
552f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_ALTERNATIVE)
553f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_PRIMARY)
554f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.STARRED)
555f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.TIMES_CONTACTED)
556cf832869bcf91b8037d8b7f510a3a213b30764a3Dmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
557f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
558f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
559f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsPresenceColumns = ProjectionMap.builder()
560f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
561f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE)
562f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
563f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
564f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
565f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
566f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
567f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
568f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
569f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
570f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
571f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
572f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
573f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
574f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
575f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
576f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSnippetColumns = ProjectionMap.builder()
57703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET)
578f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
579f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
580f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactColumns = ProjectionMap.builder()
581f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_NAME)
582f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_TYPE)
58343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(RawContacts.DATA_SET)
58443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(RawContacts.ACCOUNT_TYPE_AND_DATA_SET)
585f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DIRTY)
586f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.NAME_VERIFIED)
587f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SOURCE_ID)
588f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.VERSION)
589f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
590f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
591f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactSyncColumns = ProjectionMap.builder()
592f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC1)
593f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC2)
594f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC3)
595f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC4)
596f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
597f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
598f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataColumns = ProjectionMap.builder()
599f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA1)
600f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA2)
601f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA3)
602f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA4)
603f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA5)
604f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA6)
605f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA7)
606f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA8)
607f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA9)
608f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA10)
609f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA11)
610f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA12)
611f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA13)
612f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA14)
613f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA15)
614f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA_VERSION)
615f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_PRIMARY)
616f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_SUPER_PRIMARY)
617f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.MIMETYPE)
618f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RES_PACKAGE)
619f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC1)
620f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC2)
621f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC3)
622f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC4)
623f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(GroupMembership.GROUP_SOURCE_ID)
624f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
625f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
626f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactPresenceColumns = ProjectionMap.builder()
627f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
628f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.PRESENCE)
629f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
630f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.CHAT_CAPABILITY)
631f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
632f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
633f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
634f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
635f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
636f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
637f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
638f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
639f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
640f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
641f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
642f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
643f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataPresenceColumns = ProjectionMap.builder()
644f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.PRESENCE, Tables.PRESENCE + "." + StatusUpdates.PRESENCE)
645f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CHAT_CAPABILITY, Tables.PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
646f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS, StatusUpdatesColumns.CONCRETE_STATUS)
647f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_TIMESTAMP, StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
648f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_RES_PACKAGE, StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
649f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_LABEL, StatusUpdatesColumns.CONCRETE_STATUS_LABEL)
650f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_ICON, StatusUpdatesColumns.CONCRETE_STATUS_ICON)
651f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
652f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
653038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    /** Contains just BaseColumns._COUNT */
654f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sCountProjectionMap = ProjectionMap.builder()
655f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(BaseColumns._COUNT, "COUNT(*)")
656f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
657f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
658e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    /** Contains just the contacts columns */
659f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionMap = ProjectionMap.builder()
660f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts._ID)
661f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
662f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.NAME_RAW_CONTACT_ID)
66324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(Contacts.IS_USER_PROFILE)
664f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
665f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsPresenceColumns)
666f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
667f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
668916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Contains just the contacts columns */
669f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionWithSnippetMap = ProjectionMap.builder()
670f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
671f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sSnippetColumns)
672f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
673916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
6745e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    /** Used for pushing starred contacts to the top of a times contacted list **/
675f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentStarredProjectionMap = ProjectionMap.builder()
676f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
6772f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, String.valueOf(Long.MAX_VALUE))
678f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
679f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
680f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentFrequentProjectionMap = ProjectionMap.builder()
681f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
6822f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, "SUM(" + DataUsageStatColumns.CONCRETE_TIMES_USED + ")")
683f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
684f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6854928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    /**
6864928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * Used for Strequent Uri with {@link ContactsContract#STREQUENT_PHONE_ONLY}, which allows
6874928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * users to obtain part of Data columns. Right now Starred part just returns NULL for
6884928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * those data columns (frequent part should return real ones in data table).
6894928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     **/
6904928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    private static final ProjectionMap sStrequentPhoneOnlyStarredProjectionMap
6914928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            = ProjectionMap.builder()
6924928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .addAll(sContactsProjectionMap)
6934928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, String.valueOf(Long.MAX_VALUE))
6944928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.NUMBER, "NULL")
6954928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.TYPE, "NULL")
6964928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.LABEL, "NULL")
6974928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .build();
6984928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
6994928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    /**
7004928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * Used for Strequent Uri with {@link ContactsContract#STREQUENT_PHONE_ONLY}, which allows
7014928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * users to obtain part of Data columns. We hard-code {@link Contacts#IS_USER_PROFILE} to NULL,
7024928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * because sContactsProjectionMap specifies a field that doesn't exist in the view behind the
7034928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * query that uses this projection map.
7044928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     **/
7054928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    private static final ProjectionMap sStrequentPhoneOnlyFrequentProjectionMap
7064928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            = ProjectionMap.builder()
7074928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .addAll(sContactsProjectionMap)
7084928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, DataUsageStatColumns.CONCRETE_TIMES_USED)
7094928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.NUMBER)
7104928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.TYPE)
7114928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.LABEL)
7124928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Contacts.IS_USER_PROFILE, "NULL")
7134928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .build();
7144928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
715f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    /** Contains just the contacts vCard columns */
716f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsVCardProjectionMap = ProjectionMap.builder()
717fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            .add(Contacts._ID)
718f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.DISPLAY_NAME, Contacts.DISPLAY_NAME + " || '.vcf'")
719f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.SIZE, "NULL")
720f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
721f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
722ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    /** Contains just the raw contacts columns */
723f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactsProjectionMap = ProjectionMap.builder()
724f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
725f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
726f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
727f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_PRIMARY)
728f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_ALTERNATIVE)
729f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_SOURCE)
730f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME)
731f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME_STYLE)
732f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_PRIMARY)
733f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_ALTERNATIVE)
734f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.TIMES_CONTACTED)
735f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.LAST_TIME_CONTACTED)
736f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CUSTOM_RINGTONE)
737f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SEND_TO_VOICEMAIL)
738f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
739f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.AGGREGATION_MODE)
74024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
741f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
742f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
743f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
744f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
745a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the raw entity view*/
746f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawEntityProjectionMap = ProjectionMap.builder()
747f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
748f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
749f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.Entity.DATA_ID)
750f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
751f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
75224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
753f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
754f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
755f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
756f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
757f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
758a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the contact entity view*/
759f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sEntityProjectionMap = ProjectionMap.builder()
760f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity._ID)
761f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.CONTACT_ID)
762f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.RAW_CONTACT_ID)
763f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DATA_ID)
764f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.NAME_RAW_CONTACT_ID)
765f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DELETED)
76624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(Contacts.IS_USER_PROFILE)
767f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
768f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
769f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
770f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
771f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
772f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
773f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
774f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
7754a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /** Contains columns from the data view */
776f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataProjectionMap = ProjectionMap.builder()
777f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID)
778f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RAW_CONTACT_ID)
779f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CONTACT_ID)
780f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.NAME_RAW_CONTACT_ID)
78124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
782f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
783f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
784f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
785f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
786f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
787f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
788f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
7895e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /** Contains columns from the data view */
790f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDistinctDataProjectionMap = ProjectionMap.builder()
791f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID, "MIN(" + Data._ID + ")")
792f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
79324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
794f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
795f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
796f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
797f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
798f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
799f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
8009261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
801f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sPhoneLookupProjectionMap = ProjectionMap.builder()
802f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup._ID, "contacts_view." + Contacts._ID)
803f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LOOKUP_KEY, "contacts_view." + Contacts.LOOKUP_KEY)
804f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.DISPLAY_NAME, "contacts_view." + Contacts.DISPLAY_NAME)
805f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LAST_TIME_CONTACTED, "contacts_view." + Contacts.LAST_TIME_CONTACTED)
806f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TIMES_CONTACTED, "contacts_view." + Contacts.TIMES_CONTACTED)
807f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.STARRED, "contacts_view." + Contacts.STARRED)
808f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.IN_VISIBLE_GROUP, "contacts_view." + Contacts.IN_VISIBLE_GROUP)
809f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.PHOTO_ID, "contacts_view." + Contacts.PHOTO_ID)
8103d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_URI, "contacts_view." + Contacts.PHOTO_URI)
8113d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_THUMBNAIL_URI, "contacts_view." + Contacts.PHOTO_THUMBNAIL_URI)
812f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.CUSTOM_RINGTONE, "contacts_view." + Contacts.CUSTOM_RINGTONE)
813f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.HAS_PHONE_NUMBER, "contacts_view." + Contacts.HAS_PHONE_NUMBER)
814f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.SEND_TO_VOICEMAIL, "contacts_view." + Contacts.SEND_TO_VOICEMAIL)
815f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.NUMBER, Phone.NUMBER)
816f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TYPE, Phone.TYPE)
817f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LABEL, Phone.LABEL)
8182530512f639c4979fd7371c7dd25dd67e8118124Bai Tao            .add(PhoneLookup.NORMALIZED_NUMBER, Phone.NORMALIZED_NUMBER)
819f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
820f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
821ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
822f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsProjectionMap = ProjectionMap.builder()
823f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups._ID)
824f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_NAME)
825f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_TYPE)
82643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(Groups.DATA_SET)
82743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(Groups.ACCOUNT_TYPE_AND_DATA_SET)
828f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SOURCE_ID)
829f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DIRTY)
830f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.VERSION)
831f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.RES_PACKAGE)
832f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE)
833f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE_RES)
834f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.GROUP_VISIBLE)
835f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYSTEM_ID)
836f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DELETED)
837f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.NOTES)
838f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SHOULD_SYNC)
839f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.FAVORITES)
840f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.AUTO_ADD)
841c039cfb78c40730483fd71178df63ada5826a315Dmitri Plotnikov            .add(Groups.GROUP_IS_READ_ONLY)
842f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC1)
843f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC2)
844f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC3)
845f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC4)
846f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
847f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
848ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains {@link Groups} columns along with summary details */
849f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsSummaryProjectionMap = ProjectionMap.builder()
850f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sGroupsProjectionMap)
851f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_COUNT,
852f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                    "(SELECT COUNT(" + ContactsColumns.CONCRETE_ID + ") FROM "
853f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + Tables.CONTACTS_JOIN_RAW_CONTACTS_DATA_FILTERED_BY_GROUPMEMBERSHIP
854f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + ")")
855f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_WITH_PHONES,
856f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                    "(SELECT COUNT(" + ContactsColumns.CONCRETE_ID + ") FROM "
857f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + Tables.CONTACTS_JOIN_RAW_CONTACTS_DATA_FILTERED_BY_GROUPMEMBERSHIP
858f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + " WHERE " + Contacts.HAS_PHONE_NUMBER + ")")
859f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            .build();
860f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa
861f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa    // This is only exposed as hidden API for the contacts app, so we can be very specific in
862f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa    // the filtering
863f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa    private static final ProjectionMap sGroupsSummaryProjectionMapWithGroupCountPerAccount =
864f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            ProjectionMap.builder()
865f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            .addAll(sGroupsSummaryProjectionMap)
866f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            .add(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT,
867f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                    "(SELECT COUNT(*) FROM " + Views.GROUPS + " WHERE "
868f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + "(" + Groups.ACCOUNT_NAME + "="
869f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + GroupsColumns.CONCRETE_ACCOUNT_NAME
870f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + " AND "
871f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.ACCOUNT_TYPE + "=" + GroupsColumns.CONCRETE_ACCOUNT_TYPE
872f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + " AND "
873f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.DELETED + "=0 AND "
874f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.FAVORITES + "=0 AND "
875f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.AUTO_ADD + "=0"
876f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + ")"
877f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + " GROUP BY "
878f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.ACCOUNT_NAME + ", " + Groups.ACCOUNT_TYPE
879f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                   + ")")
880f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
881f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
882373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains the agg_exceptions columns */
883f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sAggregationExceptionsProjectionMap = ProjectionMap.builder()
884f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id")
885f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.TYPE)
886f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID1)
887f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID2)
888f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
889f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
890eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    /** Contains the agg_exceptions columns */
891f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSettingsProjectionMap = ProjectionMap.builder()
892f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_NAME)
893f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_TYPE)
894f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            .add(Settings.DATA_SET)
895f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_VISIBLE)
896f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.SHOULD_SYNC)
897f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ANY_UNSYNCED,
898f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN MIN(" + Settings.SHOULD_SYNC
899f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                        + ",(SELECT "
900f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + "(CASE WHEN MIN(" + Groups.SHOULD_SYNC + ") IS NULL"
901f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " THEN 1"
902f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " ELSE MIN(" + Groups.SHOULD_SYNC + ")"
903f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " END)"
904f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.GROUPS
905f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
906f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_NAME
907f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
908f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                                    + SettingsColumns.CONCRETE_ACCOUNT_TYPE
909f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                                + " AND ((" + GroupsColumns.CONCRETE_DATA_SET + " IS NULL AND "
910f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                                    + SettingsColumns.CONCRETE_DATA_SET + " IS NULL) OR ("
911f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                                    + GroupsColumns.CONCRETE_DATA_SET + "="
912f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                                    + SettingsColumns.CONCRETE_DATA_SET + "))))=0"
913f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN 1"
914f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE 0"
915f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " END)")
916f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_COUNT,
917f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
918f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
919f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
920f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
921f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
922f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
923f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_WITH_PHONES,
924f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
925f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
926f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
927f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + Contacts.HAS_PHONE_NUMBER
928f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
929f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
930f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
931f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
932f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
93382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Contains StatusUpdates columns */
934f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStatusUpdatesProjectionMap = ProjectionMap.builder()
935f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PresenceColumns.RAW_CONTACT_ID)
936f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.DATA_ID, DataColumns.CONCRETE_ID)
937f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_ACCOUNT)
938f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_HANDLE)
939f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PROTOCOL)
940f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // We cannot allow a null in the custom protocol field, because SQLite3 does not
941f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // properly enforce uniqueness of null values
942f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CUSTOM_PROTOCOL,
943f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN " + StatusUpdates.CUSTOM_PROTOCOL + "=''"
944f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN NULL"
945f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE " + StatusUpdates.CUSTOM_PROTOCOL + " END)")
946f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PRESENCE)
947f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CHAT_CAPABILITY)
948f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS)
949f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_TIMESTAMP)
950f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_RES_PACKAGE)
951f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_ICON)
952f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_LABEL)
953f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
954f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
9553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /** Contains StreamItems columns */
9563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final ProjectionMap sStreamItemsProjectionMap = ProjectionMap.builder()
9579b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems._ID)
9589b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.CONTACT_ID)
959af10329f85c5d8c4196c495a9f0f9a6c6ecbc231Daniel Lehmann            .add(StreamItems.CONTACT_LOOKUP_KEY)
9609b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.ACCOUNT_NAME)
9619b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.ACCOUNT_TYPE)
9629b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.DATA_SET)
9633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RAW_CONTACT_ID)
9649b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.RAW_CONTACT_SOURCE_ID)
9653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_PACKAGE)
9663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_ICON)
9673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_LABEL)
9683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.TEXT)
9693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.TIMESTAMP)
9703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.COMMENTS)
9710bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.SYNC1)
9720bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.SYNC2)
9730bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.SYNC3)
9740bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.SYNC4)
9753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .build();
9763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
9773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final ProjectionMap sStreamItemPhotosProjectionMap = ProjectionMap.builder()
9783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos._ID, StreamItemPhotosColumns.CONCRETE_ID)
9793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RAW_CONTACT_ID)
9800bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.RAW_CONTACT_SOURCE_ID, RawContactsColumns.CONCRETE_SOURCE_ID)
9813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.STREAM_ITEM_ID)
9823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.SORT_INDEX)
9836802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            .add(StreamItemPhotos.PHOTO_FILE_ID)
9846802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            .add(StreamItemPhotos.PHOTO_URI,
9856802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    "'" + DisplayPhoto.CONTENT_URI + "'||'/'||" + StreamItemPhotos.PHOTO_FILE_ID)
9861dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.HEIGHT)
9871dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.WIDTH)
9881dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.FILESIZE)
9890bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItemPhotos.SYNC1)
9900bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItemPhotos.SYNC2)
9910bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItemPhotos.SYNC3)
9920bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItemPhotos.SYNC4)
9933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .build();
9943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
9951b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    /** Contains Live Folders columns */
996f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sLiveFoldersProjectionMap = ProjectionMap.builder()
997f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders._ID, Contacts._ID)
998f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders.NAME, Contacts.DISPLAY_NAME)
999f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // TODO: Put contact photo back when we have a way to display a default icon
1000f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // for contacts without a photo
1001f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // .add(LiveFolders.ICON_BITMAP, Photos.DATA)
1002f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
1003f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
1004d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /** Contains {@link Directory} columns */
1005f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDirectoryProjectionMap = ProjectionMap.builder()
1006f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory._ID)
1007f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.PACKAGE_NAME)
1008f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.TYPE_RESOURCE_ID)
1009f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DISPLAY_NAME)
1010f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DIRECTORY_AUTHORITY)
1011f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_TYPE)
1012f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_NAME)
1013f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.EXPORT_SUPPORT)
1014778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.SHORTCUT_SUPPORT)
1015778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.PHOTO_SUPPORT)
1016f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
10177e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
10189705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    // where clause to update the status_updates table
10199705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private static final String WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE =
10209705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdatesColumns.DATA_ID + " IN (SELECT Distinct " + StatusUpdates.DATA_ID +
10219705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " FROM " + Tables.STATUS_UPDATES + " LEFT OUTER JOIN " + Tables.PRESENCE +
10229705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " ON " + StatusUpdatesColumns.DATA_ID + " = " + StatusUpdates.DATA_ID + " WHERE ";
10239705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
10242526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private static final String[] EMPTY_STRING_ARRAY = new String[0];
10252526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
1026bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1027bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Notification ID for failure to import contacts.
1028bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1029bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private static final int LEGACY_IMPORT_FAILED_NOTIFICATION = 1;
103051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
103103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_START_MATCH = "[";
103203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_END_MATCH = "]";
103303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_ELLIPSIS = "...";
103403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final int DEFAULT_SNIPPET_ARG_MAX_TOKENS = -10;
103503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
10369a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    private boolean sIsPhoneInitialized;
10379a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    private boolean sIsPhone;
10389a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
1039f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private StringBuilder mSb = new StringBuilder();
10401129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs1 = new String[1];
10411129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs2 = new String[2];
10422526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private ArrayList<String> mSelectionArgs = Lists.newArrayList();
10432526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
1044f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private Account mAccount;
1045f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
104646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
104746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Stores mapping from type Strings exposed via {@link DataUsageFeedback} to
104846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * type integers in {@link DataUsageStatColumns}.
104946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
105046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final Map<String, Integer> sDataUsageTypeMap;
105146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
10524f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
10534f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
1054a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
1055d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
1056d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
1057a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_ID_DATA);
1058a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/entities", CONTACTS_ID_ENTITIES);
10593653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
10603653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
10612d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*",
10622d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                AGGREGATION_SUGGESTIONS);
1063a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_ID_PHOTO);
1064f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/display_photo",
1065f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_ID_DISPLAY_PHOTO);
10663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/stream_items",
10673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_ID_STREAM_ITEMS);
1068c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter", CONTACTS_FILTER);
10695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER);
10705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP);
10712149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/data", CONTACTS_LOOKUP_DATA);
1072bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/photo",
1073bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                CONTACTS_LOOKUP_PHOTO);
10745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
10752149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/data",
10762149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                CONTACTS_LOOKUP_ID_DATA);
1077bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/photo",
1078bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                CONTACTS_LOOKUP_ID_PHOTO);
1079f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/display_photo",
1080f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_LOOKUP_DISPLAY_PHOTO);
1081f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/display_photo",
1082f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_LOOKUP_ID_DISPLAY_PHOTO);
1083a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/entities",
1084a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ENTITIES);
1085a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/entities",
1086a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ID_ENTITIES);
10873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/stream_items",
10883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_LOOKUP_STREAM_ITEMS);
10893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/stream_items",
10903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_LOOKUP_ID_STREAM_ITEMS);
1091f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_vcard/*", CONTACTS_AS_VCARD);
109242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_multi_vcard/*",
109342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                CONTACTS_AS_MULTI_VCARD);
10945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT);
1095ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*",
1096ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                CONTACTS_STREQUENT_FILTER);
10975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP);
109845ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa        matcher.addURI(ContactsContract.AUTHORITY, "contacts/frequent", CONTACTS_FREQUENT);
10993653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
11005ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
11015ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
11025ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA);
1103f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/display_photo",
1104f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                RAW_CONTACTS_ID_DISPLAY_PHOTO);
110546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ENTITY_ID);
11063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/stream_items",
11073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                RAW_CONTACTS_ID_STREAM_ITEMS);
110882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/stream_items/#",
110982780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                RAW_CONTACTS_ID_STREAM_ITEMS_ID);
111046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
111146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contact_entities", RAW_CONTACT_ENTITIES);
1112b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
11134f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
11144f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
1115ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
111648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID);
11175e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER);
1118ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
11194a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS);
112048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID);
11211dac83b8fa58944acfd00f44e717a7dddc659d2dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup", EMAILS_LOOKUP);
11225e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP);
11235e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER);
11244a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
1125ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
112648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
112746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        /** "*" is in CSV form with data ids ("123,456,789") */
112846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        matcher.addURI(ContactsContract.AUTHORITY, "data/usagefeedback/*", DATA_USAGE_FEEDBACK_ID);
11291f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1130ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
1131ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
1132ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
1133ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
113435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
1135b5a4add17815167d20a90645779df34cdf45280dFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#",
1136b5a4add17815167d20a90645779df34cdf45280dFred Quintana                SYNCSTATE_ID);
11375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/" + SyncStateContentProviderHelper.PATH,
11385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                PROFILE_SYNCSTATE);
11395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY,
11405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                "profile/" + SyncStateContentProviderHelper.PATH + "/#",
11415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                PROFILE_SYNCSTATE_ID);
114235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1143a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
1144b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
1145b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
1146b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
1147b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
11484f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1149eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS);
1150eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
115182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates", STATUS_UPDATES);
115282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates/#", STATUS_UPDATES_ID);
11531f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1154c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
1155c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
1156c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
1157c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
11582d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*",
1159c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SHORTCUT);
1160c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
11611b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts",
11621b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS);
11631b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts/*",
11641b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_GROUP_NAME);
11651b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts_with_phones",
11661b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_WITH_PHONES);
11671b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/favorites",
11681b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_FAVORITES);
116909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
117009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "provider_status", PROVIDER_STATUS);
1171d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
1172d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories", DIRECTORIES);
1173d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories/#", DIRECTORIES_ID);
11747a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
11757a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "complete_name", COMPLETE_NAME);
117624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
117724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile", PROFILE);
117824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/entities", PROFILE_ENTITIES);
117924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/data", PROFILE_DATA);
118024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/data/#", PROFILE_DATA_ID);
118124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/as_vcard", PROFILE_AS_VCARD);
118224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts", PROFILE_RAW_CONTACTS);
118324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#",
118424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID);
118524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/data",
118624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID_DATA);
118724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/entity",
118824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID_ENTITIES);
11895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/status_updates",
11905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                PROFILE_STATUS_UPDATES);
11913202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contact_entities",
11923202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro                PROFILE_RAW_CONTACT_ENTITIES);
119346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
11943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items", STREAM_ITEMS);
11953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/photo", STREAM_ITEMS_PHOTOS);
11963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#", STREAM_ITEMS_ID);
11973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#/photo", STREAM_ITEMS_ID_PHOTOS);
11983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#/photo/#",
11993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                STREAM_ITEMS_ID_PHOTOS_ID);
12003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items_limit", STREAM_ITEMS_LIMIT);
12013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
12025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "display_photo/#", DISPLAY_PHOTO);
1203f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "photo_dimensions", PHOTO_DIMENSIONS);
1204f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
120546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        HashMap<String, Integer> tmpTypeMap = new HashMap<String, Integer>();
120646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_CALL, DataUsageStatColumns.USAGE_TYPE_INT_CALL);
120746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_LONG_TEXT,
120846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT);
120946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_SHORT_TEXT,
121046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DataUsageStatColumns.USAGE_TYPE_INT_SHORT_TEXT);
121146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        sDataUsageTypeMap = Collections.unmodifiableMap(tmpTypeMap);
121219a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    }
121319a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
1214d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static class DirectoryInfo {
1215d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String authority;
1216d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountName;
1217d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountType;
1218d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
1219d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
1220d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
1221d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Cached information about contact directories.
1222d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
12234458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private HashMap<String, DirectoryInfo> mDirectoryCache = new HashMap<String, DirectoryInfo>();
12244458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private boolean mDirectoryCacheValid = false;
1225d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
12263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
122743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * An entry in group id cache. It maps the combination of (account type, account name, data set,
1228ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * and source id) to group row id.
1229ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     */
1230e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    public static class GroupIdCacheEntry {
1231ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType;
1232ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName;
123343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String dataSet;
1234ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String sourceId;
1235ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        long groupId;
1236ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    }
1237a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1238e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // We don't need a soft cache for groups - the assumption is that there will only
1239e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // be a small number of contact groups. The cache is keyed off source id.  The value
1240e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // is a list of groups with this group id.
1241e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    private HashMap<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache = Maps.newHashMap();
1242e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov
124324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
1244f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Maximum dimension (height or width) of display photos.  Larger images will be scaled
1245f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * to fit.
1246f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
1247f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private int mMaxDisplayPhotoDim;
1248f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1249f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
1250f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Maximum dimension (height or width) of photo thumbnails.
1251f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
1252f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private int mMaxThumbnailPhotoDim;
1253f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
12545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
12555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Sub-provider for handling profile requests against the profile database.
12565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
12575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private ProfileProvider mProfileProvider;
1258f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
12594097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
1260f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private NameLookupBuilder mNameLookupBuilder;
1261315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
1262622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private PostalSplitter mPostalSplitter;
1263622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
126472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    private ContactDirectoryManager mContactDirectoryManager;
12655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
12665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
12675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * The active (thread-local) database.  This will be switched between a contacts-specific
12685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * database and a profile-specific database, depending on what the current operation is
12695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * targeted to.
12705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
12715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<SQLiteDatabase> mActiveDb = new ThreadLocal<SQLiteDatabase>();
12725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
12735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // This variable keeps track of whether the current operation is intended for the profile DB.
12745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<Boolean> mInProfileMode = new ThreadLocal<Boolean>();
12755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
12765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Whether we're currently in the process of applying a batch of operations.
12775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<Boolean> mApplyingBatch = new ThreadLocal<Boolean>();
12785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
12795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Separate data row handler instances for contact data and profile data.
12805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private HashMap<String, DataRowHandler> mDataRowHandlers;
12815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private HashMap<String, DataRowHandler> mProfileDataRowHandlers;
12825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
12835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Depending on whether the action being performed is for the profile, we will use one of two
12845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // database helper instances.
12855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<ContactsDatabaseHelper> mDbHelper =
12865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            new ThreadLocal<ContactsDatabaseHelper>();
12875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private ContactsDatabaseHelper mContactsHelper;
12885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private ProfileDatabaseHelper mProfileHelper;
12895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
12905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Depending on whether the action being performed is for the profile or not, we will use one of
12915d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // two aggregator instances.
12925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<ContactAggregator> mAggregator = new ThreadLocal<ContactAggregator>();
1293622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private ContactAggregator mContactAggregator;
12945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private ContactAggregator mProfileAggregator;
12955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
12965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Depending on whether the action being performed is for the profile or not, we will use one of
12975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // two photo store instances (with their files stored in separate subdirectories).
12985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<PhotoStore> mPhotoStore = new ThreadLocal<PhotoStore>();
12995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private PhotoStore mContactsPhotoStore;
13005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private PhotoStore mProfilePhotoStore;
13015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
13025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // The active transaction context will switch depending on the operation being performed.
13035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Both transaction contexts will be cleared out when a batch transaction is started, and
13045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // each will be processed separately when a batch transaction completes.
13055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private TransactionContext mContactTransactionContext = new TransactionContext(false);
13065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private TransactionContext mProfileTransactionContext = new TransactionContext(true);
13075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<TransactionContext> mTransactionContext =
13085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            new ThreadLocal<TransactionContext>();
13095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
13105d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // This database reference will only be referenced when a batch operation is in progress
13115d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // that includes profile DB operations.  It is used to create and handle a separate transaction
13125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // around that batch.  Outside of such a batch operation, this will be null.
13135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<SQLiteDatabase> mProfileDbForBatch =
13145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            new ThreadLocal<SQLiteDatabase>();
13155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
13165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // This flag is set during a batch operation that involves the profile DB to indicate that
13175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // errors occurred during processing of one of the profile operations.
13185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<Boolean> mProfileErrorsInBatch = new ThreadLocal<Boolean>();
13195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
1320f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
1321a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    private GlobalSearchSupport mGlobalSearchSupport;
1322d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov    private CommonNicknameCache mCommonNicknameCache;
1323f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    private SearchIndexManager mSearchIndexManager;
1324a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
132520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private ContentValues mValues = new ContentValues();
132673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    private HashMap<String, Boolean> mAccountWritability = Maps.newHashMap();
132720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
132809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private int mProviderStatus = ProviderStatus.STATUS_NORMAL;
13293826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private boolean mProviderStatusUpdateNeeded;
133009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private long mEstimatedStorageRequirement = 0;
133115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private volatile CountDownLatch mReadAccessLatch;
133215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private volatile CountDownLatch mWriteAccessLatch;
133315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private boolean mAccountUpdateListenerRegistered;
1334bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private boolean mOkToOpenAccess = true;
133573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
13361a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey    private boolean mVisibleTouched = false;
13371a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
133881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    private boolean mSyncToNetwork;
133981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
13404cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    private Locale mCurrentLocale;
13413826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private int mContactsAccountCount;
1342d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov
1343bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private HandlerThread mBackgroundThread;
1344bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private Handler mBackgroundHandler;
1345bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1346f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private long mLastPhotoCleanup = 0;
1347f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
13484f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
13494f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
1350663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki        if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
1351663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki            Log.d(Constants.PERFORMANCE_TAG, "ContactsProvider2.onCreate start");
1352663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki        }
1353de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        super.onCreate();
1354ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        try {
1355ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return initialize();
1356ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        } catch (RuntimeException e) {
1357ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            Log.e(TAG, "Cannot start provider", e);
1358ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return false;
1359663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki        } finally {
1360663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki            if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
1361663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki                Log.d(Constants.PERFORMANCE_TAG, "ContactsProvider2.onCreate finish");
1362663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki            }
1363ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        }
1364ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    }
136535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1366ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    private boolean initialize() {
136715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        StrictMode.setThreadPolicy(
136815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
136915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
13703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Resources resources = getContext().getResources();
1371f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mMaxDisplayPhotoDim = resources.getInteger(
1372f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                R.integer.config_max_display_photo_dim);
1373f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mMaxThumbnailPhotoDim = resources.getInteger(
1374f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                R.integer.config_max_thumbnail_photo_dim);
13753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
13765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsHelper = (ContactsDatabaseHelper) getDatabaseHelper();
13775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mDbHelper.set(mContactsHelper);
137872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        mContactDirectoryManager = new ContactDirectoryManager(this);
1379a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mGlobalSearchSupport = new GlobalSearchSupport(this);
138065ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov
1381bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        // The provider is closed for business until fully initialized
138215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mReadAccessLatch = new CountDownLatch(1);
138315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mWriteAccessLatch = new CountDownLatch(1);
138472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
1385bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundThread = new HandlerThread("ContactsProviderWorker",
1386bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                Process.THREAD_PRIORITY_BACKGROUND);
1387bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundThread.start();
1388bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler = new Handler(mBackgroundThread.getLooper()) {
1389bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            @Override
1390bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            public void handleMessage(Message msg) {
1391bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                performBackgroundTask(msg.what, msg.obj);
1392bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1393bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        };
13942a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov
13955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Set up the sub-provider for handling profiles.
13965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileProvider = getProfileProvider();
13975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        ProviderInfo profileInfo = new ProviderInfo();
13985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        profileInfo.readPermission = "android.permission.READ_PROFILE";
13995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        profileInfo.writePermission = "android.permission.WRITE_PROFILE";
14005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileProvider.attachInfo(getContext(), profileInfo);
14015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileHelper = (ProfileDatabaseHelper) mProfileProvider.getDatabaseHelper();
14025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
140315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_INITIALIZE);
1404bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS);
1405bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
1406bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_LOCALE);
1407bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM);
140805e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_SEARCH_INDEX);
1409bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_PROVIDER_STATUS);
141015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_OPEN_WRITE_ACCESS);
1411f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
14123826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
141349d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov        return true;
14144f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
14154f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1416767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    /**
141751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * (Re)allocates all locale-sensitive structures.
141851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
141904b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov    private void initForDefaultLocale() {
142015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        Context context = getContext();
14215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mLegacyApiSupport = new LegacyApiSupport(context, mContactsHelper, this,
14225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mGlobalSearchSupport);
14234cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mCurrentLocale = getLocale();
14245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mNameSplitter = mContactsHelper.createNameSplitter();
14254cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
14264cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mPostalSplitter = new PostalSplitter(mCurrentLocale);
14275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mCommonNicknameCache = new CommonNicknameCache(mContactsHelper.getReadableDatabase());
1428cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao        ContactLocaleUtils.getIntance().setLocale(mCurrentLocale);
14295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactAggregator = new ContactAggregator(this, mContactsHelper,
143015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                createPhotoPriorityResolver(context), mNameSplitter, mCommonNicknameCache);
14315b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
14325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileAggregator = new ProfileAggregator(this, mProfileHelper,
14335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                createPhotoPriorityResolver(context), mNameSplitter, mCommonNicknameCache);
14345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
1435f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov        mSearchIndexManager = new SearchIndexManager(this);
14365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
14375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsPhotoStore = new PhotoStore(getContext().getFilesDir(), mContactsHelper);
14385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfilePhotoStore = new PhotoStore(new File(getContext().getFilesDir(), "profile"),
14395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mProfileHelper);
14405b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
1441bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers = new HashMap<String, DataRowHandler>();
14425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        initDataRowHandlers(mDataRowHandlers, mContactsHelper, mContactAggregator,
14435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mContactsPhotoStore);
14445d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileDataRowHandlers = new HashMap<String, DataRowHandler>();
14455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        initDataRowHandlers(mProfileDataRowHandlers, mProfileHelper, mProfileAggregator,
14465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mProfilePhotoStore);
14475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
14485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Set initial thread-local state variables for the Contacts DB.
14495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        switchToContactMode();
14505d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
1451bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
14525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private void initDataRowHandlers(Map<String, DataRowHandler> handlerMap,
14535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            ContactsDatabaseHelper dbHelper, ContactAggregator contactAggregator,
14545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            PhotoStore photoStore) {
14555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Context context = getContext();
14565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Email.CONTENT_ITEM_TYPE,
14575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForEmail(context, dbHelper, contactAggregator));
14585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Im.CONTENT_ITEM_TYPE,
14595d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForIm(context, dbHelper, contactAggregator));
14605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Organization.CONTENT_ITEM_TYPE,
14615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForOrganization(context, dbHelper, contactAggregator));
14625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Phone.CONTENT_ITEM_TYPE,
14635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForPhoneNumber(context, dbHelper, contactAggregator));
14645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Nickname.CONTENT_ITEM_TYPE,
14655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForNickname(context, dbHelper, contactAggregator));
14665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(StructuredName.CONTENT_ITEM_TYPE,
14675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForStructuredName(context, dbHelper, contactAggregator,
1468bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mNameSplitter, mNameLookupBuilder));
14695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(StructuredPostal.CONTENT_ITEM_TYPE,
14705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForStructuredPostal(context, dbHelper, contactAggregator,
1471bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mPostalSplitter));
14725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(GroupMembership.CONTENT_ITEM_TYPE,
14735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForGroupMembership(context, dbHelper, contactAggregator,
1474bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mGroupIdCache));
14755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Photo.CONTENT_ITEM_TYPE,
14765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForPhoto(context, dbHelper, contactAggregator, photoStore));
14775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Note.CONTENT_ITEM_TYPE,
14785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForNote(context, dbHelper, contactAggregator));
1479bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1480bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1481bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    /**
1482bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     * Visible for testing.
1483bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     */
1484bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    /* package */ PhotoPriorityResolver createPhotoPriorityResolver(Context context) {
1485bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        return new PhotoPriorityResolver(context);
1486bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1487bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1488bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task) {
1489bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendEmptyMessage(task);
1490bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1491bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1492bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task, Object arg) {
1493bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendMessage(mBackgroundHandler.obtainMessage(task, arg));
1494bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1495bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1496bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void performBackgroundTask(int task, Object arg) {
1497bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        switch (task) {
149815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_INITIALIZE: {
149915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                initForDefaultLocale();
150015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch.countDown();
150115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch = null;
150215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                break;
150315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            }
150415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
150515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_OPEN_WRITE_ACCESS: {
1506bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (mOkToOpenAccess) {
150715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch.countDown();
150815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch = null;
1509bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1510bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1511bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1512bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1513bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS: {
1514bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (isLegacyContactImportNeeded()) {
1515bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    importLegacyContactsInBackground();
1516bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1517bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1518bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1519bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1520bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_ACCOUNTS: {
152115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Context context = getContext();
152215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                if (!mAccountUpdateListenerRegistered) {
152315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    AccountManager.get(context).addOnAccountsUpdatedListener(this, null, false);
152415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mAccountUpdateListenerRegistered = true;
152515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                }
152615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
15275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                // Update the accounts for both the contacts and profile DBs.
152815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Account[] accounts = AccountManager.get(context).getAccounts();
15295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                switchToContactMode();
1530bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                boolean accountsChanged = updateAccountsInBackground(accounts);
15315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                switchToProfileMode();
15325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                accountsChanged |= updateAccountsInBackground(accounts);
15335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
1534bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateContactsAccountCount(accounts);
1535bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateDirectoriesInBackground(accountsChanged);
1536bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1537bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1538bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1539bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_LOCALE: {
1540bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateLocaleInBackground();
1541bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1542bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1543bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1544fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            case BACKGROUND_TASK_CHANGE_LOCALE: {
1545fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                changeLocaleInBackground();
1546fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                break;
1547fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            }
1548fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1549bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM: {
1550bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (isAggregationUpgradeNeeded()) {
1551bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    upgradeAggregationAlgorithmInBackground();
1552bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1553bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1554bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1555bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
155605e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_SEARCH_INDEX: {
155705e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                updateSearchIndexInBackground();
155805e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                break;
155905e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            }
156005e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
1561bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_PROVIDER_STATUS: {
1562bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateProviderStatus();
1563bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1564bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1565bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1566bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_DIRECTORIES: {
1567bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (arg != null) {
1568bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    mContactDirectoryManager.onPackageChanged((String) arg);
1569bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1570bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1571bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1572f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1573f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case BACKGROUND_TASK_CLEANUP_PHOTOS: {
1574f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Check rate limit.
1575f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long now = System.currentTimeMillis();
1576f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (now - mLastPhotoCleanup > PHOTO_CLEANUP_RATE_LIMIT) {
1577f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    mLastPhotoCleanup = now;
15785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
15795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    // Clean up photo stores for both contacts and profiles.
15805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    switchToContactMode();
15815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    cleanupPhotoStore();
15825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    switchToProfileMode();
1583f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    cleanupPhotoStore();
1584f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    break;
1585f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
1586f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1587bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
15884cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
15894cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao
159053fac8f99f3884c372c907a76766d27fa9e1d95fDmitri Plotnikov    public void onLocaleChanged() {
15913826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
15923826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
15934f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov            return;
15944f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov        }
15954f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov
1596fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_CHANGE_LOCALE);
15974cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
159851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
159951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    /**
160051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * Verifies that the contacts database is properly configured for the current locale.
160151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * If not, changes the database locale to the current locale using an asynchronous task.
160251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * This needs to be done asynchronously because the process involves rebuilding
160351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * large data structures (name lookup, sort keys), which can take minutes on
160451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * a large set of contacts.
160551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
1606bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateLocaleInBackground() {
1607f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
1608f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        // The process is already running - postpone the change
1609f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        if (mProviderStatus == ProviderStatus.STATUS_CHANGING_LOCALE) {
1610f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov            return;
1611f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        }
1612f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
161351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
161451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final String providerLocale = prefs.getString(PREF_LOCALE, null);
161551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final Locale currentLocale = mCurrentLocale;
161651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        if (currentLocale.toString().equals(providerLocale)) {
161751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            return;
161851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        }
161951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
162051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        int providerStatus = mProviderStatus;
162151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_CHANGING_LOCALE);
16225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsHelper.setLocale(this, currentLocale);
16235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileHelper.setLocale(this, currentLocale);
1624bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        prefs.edit().putString(PREF_LOCALE, currentLocale.toString()).apply();
1625bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        setProviderStatus(providerStatus);
1626bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
162751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
1628fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    /**
1629fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     * Reinitializes the provider for a new locale.
1630fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     */
1631fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    private void changeLocaleInBackground() {
1632fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Re-initializing the provider without stopping it.
1633fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Locking the database will prevent inserts/updates/deletes from
1634fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // running at the same time, but queries may still be running
1635fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // on other threads. Those queries may return inconsistent results.
16365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        SQLiteDatabase db = mContactsHelper.getWritableDatabase();
16375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        SQLiteDatabase profileDb = mProfileHelper.getWritableDatabase();
1638fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        db.beginTransaction();
16395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        profileDb.beginTransaction();
1640fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        try {
1641fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            initForDefaultLocale();
1642fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.setTransactionSuccessful();
16435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            profileDb.setTransactionSuccessful();
1644fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        } finally {
1645fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.endTransaction();
16465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            profileDb.endTransaction();
1647fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        }
1648fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1649fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        updateLocaleInBackground();
1650fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    }
1651fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
165205e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    protected void updateSearchIndexInBackground() {
165305e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        mSearchIndexManager.updateIndex();
165405e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    }
165505e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
1656bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateDirectoriesInBackground(boolean rescan) {
1657bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mContactDirectoryManager.scanAllPackages(rescan);
165851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
165951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
16603826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateProviderStatus() {
16613826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
16623826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
16633826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return;
16643826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
16653826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
16663e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson        // No accounts/no contacts status is true if there are no account and
16675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // there are no contacts or one profile contact
16683e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson        if (mContactsAccountCount == 0) {
16695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            long contactsNum = DatabaseUtils.queryNumEntries(mContactsHelper.getReadableDatabase(),
16703e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                    Tables.CONTACTS, null);
16715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            long profileNum = DatabaseUtils.queryNumEntries(mProfileHelper.getReadableDatabase(),
16725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Tables.CONTACTS, null);
16735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
16745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            // TODO: Different status if there is a profile but no contacts?
16755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (contactsNum == 0 && profileNum <= 1) {
16763e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                setProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS);
16773e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson            } else {
16783e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                setProviderStatus(ProviderStatus.STATUS_NORMAL);
16793e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson            }
16803826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } else {
16813826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            setProviderStatus(ProviderStatus.STATUS_NORMAL);
16823826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
16833826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
16843826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
168531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
1686f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    protected void cleanupPhotoStore() {
16875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
16886802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
16896802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Assemble the set of photo store file IDs that are in use, and send those to the photo
1690f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // store.  Any photos that aren't in that set will be deleted, and any photos that no
1691f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // longer exist in the photo store will be returned for us to clear out in the DB.
16927cf50494501938f175d288077145acf49da8f171Daniel Lehmann        long photoMimeTypeId = mDbHelper.get().getMimeTypeId(Photo.CONTENT_ITEM_TYPE);
16936802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Cursor c = db.query(Views.DATA, new String[]{Data._ID, Photo.PHOTO_FILE_ID},
16947cf50494501938f175d288077145acf49da8f171Daniel Lehmann                DataColumns.MIMETYPE_ID + "=" + photoMimeTypeId + " AND "
1695f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        + Photo.PHOTO_FILE_ID + " IS NOT NULL", null, null, null, null);
16966802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Set<Long> usedPhotoFileIds = Sets.newHashSet();
16976802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> photoFileIdToDataId = Maps.newHashMap();
1698f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        try {
1699f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            while (c.moveToNext()) {
17006802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long dataId = c.getLong(0);
17016802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long photoFileId = c.getLong(1);
17026802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                usedPhotoFileIds.add(photoFileId);
17036802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                photoFileIdToDataId.put(photoFileId, dataId);
17046802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
17056802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        } finally {
17066802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            c.close();
17076802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
17086802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
17096802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Also query for all social stream item photos.
1710c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro        c = db.query(Tables.STREAM_ITEM_PHOTOS + " JOIN " + Tables.STREAM_ITEMS
1711c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                + " ON " + StreamItemPhotos.STREAM_ITEM_ID + "=" + StreamItemsColumns.CONCRETE_ID
1712c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                + " JOIN " + Tables.RAW_CONTACTS
1713c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                + " ON " + StreamItems.RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID,
17146802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                new String[]{
1715c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                        StreamItemPhotosColumns.CONCRETE_ID,
1716c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                        StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID,
1717c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                        StreamItemPhotos.PHOTO_FILE_ID,
1718c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                        RawContacts.ACCOUNT_TYPE,
1719c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                        RawContacts.ACCOUNT_NAME
17206802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                },
17216802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                null, null, null, null, null);
17226802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> photoFileIdToStreamItemPhotoId = Maps.newHashMap();
17236802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> streamItemPhotoIdToStreamItemId = Maps.newHashMap();
1724c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro        Map<Long, Account> streamItemPhotoIdToAccount = Maps.newHashMap();
17256802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        try {
17266802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            while (c.moveToNext()) {
17276802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long streamItemPhotoId = c.getLong(0);
17286802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long streamItemId = c.getLong(1);
17296802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long photoFileId = c.getLong(2);
1730c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                String accountType = c.getString(3);
1731c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                String accountName = c.getString(4);
17326802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                usedPhotoFileIds.add(photoFileId);
17336802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                photoFileIdToStreamItemPhotoId.put(photoFileId, streamItemPhotoId);
17346802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                streamItemPhotoIdToStreamItemId.put(streamItemPhotoId, streamItemId);
1735c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                Account account = new Account(accountName, accountType);
1736c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                streamItemPhotoIdToAccount.put(photoFileId, account);
1737f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1738f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } finally {
1739f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            c.close();
1740f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
1741f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1742f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // Run the photo store cleanup.
17435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> missingPhotoIds = mPhotoStore.get().cleanup(usedPhotoFileIds);
1744f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1745f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // If any of the keys we're using no longer exist, clean them up.
17466802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (!missingPhotoIds.isEmpty()) {
1747f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            ArrayList<ContentProviderOperation> ops = Lists.newArrayList();
17486802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            for (long missingPhotoId : missingPhotoIds) {
17496802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (photoFileIdToDataId.containsKey(missingPhotoId)) {
17506802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    long dataId = photoFileIdToDataId.get(missingPhotoId);
1751f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    ContentValues updateValues = new ContentValues();
1752f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    updateValues.putNull(Photo.PHOTO_FILE_ID);
1753f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    ops.add(ContentProviderOperation.newUpdate(
17546802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            ContentUris.withAppendedId(Data.CONTENT_URI, dataId))
1755f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            .withValues(updateValues).build());
1756f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
17576802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (photoFileIdToStreamItemPhotoId.containsKey(missingPhotoId)) {
17586802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // For missing photos that were in stream item photos, just delete the stream
17596802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // item photo.
17606802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    long streamItemPhotoId = photoFileIdToStreamItemPhotoId.get(missingPhotoId);
17616802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    long streamItemId = streamItemPhotoIdToStreamItemId.get(streamItemPhotoId);
1762c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                    Account account = streamItemPhotoIdToAccount.get(missingPhotoId);
17636802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    ops.add(ContentProviderOperation.newDelete(
17646802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            StreamItems.CONTENT_URI.buildUpon()
17656802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    .appendPath(String.valueOf(streamItemId))
17666802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    .appendPath(StreamItems.StreamItemPhotos.CONTENT_DIRECTORY)
17676802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    .appendPath(String.valueOf(streamItemPhotoId))
1768c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                                    .appendQueryParameter(RawContacts.ACCOUNT_NAME, account.name)
1769c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                                    .appendQueryParameter(RawContacts.ACCOUNT_TYPE, account.type)
17706802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    .build()).build());
17716802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                }
1772f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1773f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            try {
1774f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                applyBatch(ops);
1775f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            } catch (OperationApplicationException oae) {
1776f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Not a fatal problem (and we'll try again on the next cleanup).
1777f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                Log.e(TAG, "Failed to clean up outdated photo references", oae);
1778f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1779f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
1780f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
1781f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1782f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /* Visible for testing */
1783de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    @Override
1784b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
1785b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        return ContactsDatabaseHelper.getInstance(context);
178631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
178731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
17885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    public ProfileProvider getProfileProvider() {
17895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return new ProfileProvider(this);
17905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
17915d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
1792524913c66ce75ca8dec127ac88e3bc2249c246d9Dave Santoro    @VisibleForTesting
1793f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /* package */ PhotoStore getPhotoStore() {
17945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mContactsPhotoStore;
1795f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
1796f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
179787614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    /* package */ int getMaxDisplayPhotoDim() {
179887614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro        return mMaxDisplayPhotoDim;
179987614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    }
180087614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro
180187614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    /* package */ int getMaxThumbnailPhotoDim() {
180287614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro        return mMaxThumbnailPhotoDim;
180387614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    }
180487614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro
1805013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    /* package */ NameSplitter getNameSplitter() {
1806013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        return mNameSplitter;
1807013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    }
1808013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov
18095df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    /* package */ NameLookupBuilder getNameLookupBuilder() {
18105df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov        return mNameLookupBuilder;
18115df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    }
18125df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov
18135dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    /* Visible for testing */
1814ed78fd6df5e9f3a2d572162e5d374d1f4a625bddDmitri Plotnikov    public ContactDirectoryManager getContactDirectoryManagerForTest() {
181572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        return mContactDirectoryManager;
181672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
181772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
181872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    /* Visible for testing */
18195dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    protected Locale getLocale() {
18205dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        return Locale.getDefault();
18215dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
18225dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
18235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private boolean applyingBatch() {
18245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Boolean applyingBatch = mApplyingBatch.get();
18255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return applyingBatch != null && applyingBatch;
18265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
18275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
18285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private boolean inProfileMode() {
18295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Boolean profileMode = mInProfileMode.get();
18305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return profileMode != null && profileMode;
18315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
18325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
18333d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    protected boolean isLegacyContactImportNeeded() {
18345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int version = Integer.parseInt(
18355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mContactsHelper.getProperty(PROPERTY_CONTACTS_IMPORTED, "0"));
1836b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        return version < PROPERTY_CONTACTS_IMPORT_VERSION;
18373d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
18383d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1839568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected LegacyContactImporter getLegacyContactImporter() {
1840568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return new LegacyContactImporter(getContext(), this);
1841568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1842568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1843568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
1844bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     * Imports legacy contacts as a background task.
1845568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
1846bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private void importLegacyContactsInBackground() {
1847bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Importing legacy contacts");
1848bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADING);
1849568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1850bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
18515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsHelper.setLocale(this, mCurrentLocale);
1852bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        prefs.edit().putString(PREF_LOCALE, mCurrentLocale.toString()).commit();
1853568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1854bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        LegacyContactImporter importer = getLegacyContactImporter();
1855bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        if (importLegacyContacts(importer)) {
1856bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            onLegacyContactImportSuccess();
1857bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        } else {
1858bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            onLegacyContactImportFailure();
1859bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
1860568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1861568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1862bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1863bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Unlocks the provider and declares that the import process is complete.
1864bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1865bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportSuccess() {
1866bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
1867bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)getContext().getSystemService(Context.NOTIFICATION_SERVICE);
1868bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.cancel(LEGACY_IMPORT_FAILED_NOTIFICATION);
1869bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1870b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        // Store a property in the database indicating that the conversion process succeeded
18715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsHelper.setProperty(PROPERTY_CONTACTS_IMPORTED,
1872b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov                String.valueOf(PROPERTY_CONTACTS_IMPORT_VERSION));
1873bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_NORMAL);
1874bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Completed import of legacy contacts");
1875bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    }
1876bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1877bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1878bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Announces the provider status and keeps the provider locked.
1879bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1880bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportFailure() {
1881bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Context context = getContext();
1882bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
1883bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
1884bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1885bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        // Show a notification
1886bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Notification n = new Notification(android.R.drawable.stat_notify_error,
1887bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_ticker),
1888bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                System.currentTimeMillis());
1889bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.setLatestEventInfo(context,
1890bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_title),
1891bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_text),
1892bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                PendingIntent.getActivity(context, 0, new Intent(Intents.UI.LIST_DEFAULT), 0));
1893bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
1894bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1895bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.notify(LEGACY_IMPORT_FAILED_NOTIFICATION, n);
1896bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1897bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADE_OUT_OF_MEMORY);
1898bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Failed to import legacy contacts");
1899bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1900bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        // Do not let any database changes until this issue is resolved.
1901bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mOkToOpenAccess = false;
19023d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
19033d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
19043d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /* Visible for testing */
1905568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
19060e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        boolean aggregatorEnabled = mContactAggregator.isEnabled();
19073d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mContactAggregator.setEnabled(false);
19083d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        try {
1909bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            if (importer.importContacts()) {
1910bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1911bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                // TODO aggregate all newly added raw contacts
1912bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                mContactAggregator.setEnabled(aggregatorEnabled);
1913bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                return true;
1914bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
19153d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } catch (Throwable e) {
19163d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           Log.e(TAG, "Legacy contact import failed", e);
19173d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
1918bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mEstimatedStorageRequirement = importer.getEstimatedStorageRequirement();
1919bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        return false;
19203d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
19213d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1922a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
1923a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
1924a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
1925a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
19265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsHelper.wipeData();
19275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileHelper.wipeData();
19285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsPhotoStore.clear();
19295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfilePhotoStore.clear();
19303826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatus = ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS;
1931a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1932a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1933568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
193415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov     * During intialization, this content provider will
1935568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * block all attempts to change contacts data. In particular, it will hold
1936568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * up all contact syncs. As soon as the import process is complete, all
1937568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * processes waiting to write to the provider are unblocked and can proceed
1938568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * to compete for the database transaction monitor.
1939568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
194015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private void waitForAccess(CountDownLatch latch) {
194115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (latch == null) {
194215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            return;
194315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        }
194415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
194515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        while (true) {
194615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            try {
194715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                latch.await();
194815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                return;
194915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            } catch (InterruptedException e) {
195015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Thread.currentThread().interrupt();
1951ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            }
1952568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
1953568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1954568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
19555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
19565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Determines whether the given URI should be directed to the profile
19575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * database rather than the contacts database.  This is true under either
19585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * of three conditions:
19595d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * 1. The URI itself is specifically for the profile.
19605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * 2. The URI contains ID references that are in the profile ID-space.
19615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * 3. The URI contains lookup key references that match the special profile lookup key.
19625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @param uri The URI to examine.
19635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @return Whether to direct the DB operation to the profile database.
19645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
19655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private boolean mapsToProfileDb(Uri uri) {
19665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return sUriMatcher.mapsToProfile(uri);
19675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
19685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
19695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
19705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Determines whether the given URI with the given values being inserted
19715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * should be directed to the profile database rather than the contacts
19725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * database.  This is true if the URI already maps to the profile DB from
19735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * a call to {@link #mapsToProfileDb} or if the URI matches a URI that
19745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * specifies parent IDs via the ContentValues, and the given ContentValues
19755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * contains an ID in the profile ID-space.
19765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @param uri The URI to examine.
19775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @param values The values being inserted.
19785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @return Whether to direct the DB insert to the profile database.
19795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
19805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private boolean mapsToProfileDbWithInsertedValues(Uri uri, ContentValues values) {
19815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDb(uri)) {
19825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return true;
19835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
19845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int match = sUriMatcher.match(uri);
19855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (INSERT_URI_ID_VALUE_MAP.containsKey(match)) {
19865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            String idField = INSERT_URI_ID_VALUE_MAP.get(match);
19875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (values.containsKey(idField)) {
19885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long id = values.getAsLong(idField);
19895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                if (ContactsContract.isProfileId(id)) {
19905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    return true;
19915d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                }
19925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
19935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
19945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return false;
19955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
19965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
19975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
19985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Switches the provider's thread-local context variables to prepare for performing
19995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * a profile operation.
20005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
20015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private void switchToProfileMode() {
20025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mDbHelper.set(mProfileHelper);
20035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mTransactionContext.set(mProfileTransactionContext);
20045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.set(mProfileAggregator);
20055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mPhotoStore.set(mProfilePhotoStore);
20065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mInProfileMode.set(true);
20075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
20085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // If we're in batch mode and don't yet have a database set up for our transaction,
20095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // get one and start a transaction now.
20105d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (applyingBatch() && mProfileDbForBatch.get() == null) {
20115d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            SQLiteDatabase profileDb = mProfileHelper.getWritableDatabase();
20125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            profileDb.beginTransactionWithListener(this);
20135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mProfileDbForBatch.set(profileDb);
20145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
20155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
20165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
20175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
20185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Switches the provider's thread-local context variables to prepare for performing
20195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * a contacts operation.
20205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
20215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private void switchToContactMode() {
20225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mDbHelper.set(mContactsHelper);
20235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mTransactionContext.set(mContactTransactionContext);
20245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.set(mContactAggregator);
20255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mPhotoStore.set(mContactsPhotoStore);
20265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mInProfileMode.set(false);
20275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
20285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // If not in batch mode, clear out the active database - it will be set to the default
20295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // instance from SQLiteContentProvider if necessary.
20305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (!applyingBatch()) {
20315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.set(null);
20325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
20335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
20345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
2035568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2036568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
203715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
20385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDbWithInsertedValues(uri, values)) {
2039072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro            if (applyingBatch()) {
2040072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                switchToProfileMode();
2041072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                return mProfileProvider.insert(uri, values);
2042072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro            } else {
2043072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                // Start a contacts DB transaction to maintain provider synchronization.
2044072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                SQLiteDatabase contactsDb = mContactsHelper.getWritableDatabase();
2045072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                contactsDb.beginTransactionWithListener(this);
2046072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                Uri result = null;
2047072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                try {
2048072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    // Now switch to profile mode and proceed with the insert using its provider.
2049072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    switchToProfileMode();
2050072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    result = mProfileProvider.insert(uri, values);
2051072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro
2052072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    contactsDb.setTransactionSuccessful();
2053072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                } finally {
2054072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    // Finish the contacts transaction, allowing other provider operations to
2055072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    // proceed.
2056072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    contactsDb.endTransaction();
2057072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                }
2058072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                return result;
2059072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro            }
20605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } else {
20615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToContactMode();
20625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return super.insert(uri, values);
20635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
2064568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2065568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2066568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2067568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
206815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (mWriteAccessLatch != null) {
2069bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // We are stuck trying to upgrade contacts db.  The only update request
2070bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // allowed in this case is an update of provider status, which will trigger
2071bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // an attempt to upgrade contacts again.
2072bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            int match = sUriMatcher.match(uri);
2073bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            if (match == PROVIDER_STATUS) {
2074bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                Integer newStatus = values.getAsInteger(ProviderStatus.STATUS);
2075bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                if (newStatus != null && newStatus == ProviderStatus.STATUS_UPGRADING) {
2076bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    scheduleBackgroundTask(BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS);
2077bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 1;
2078bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                } else {
2079bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 0;
2080bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                }
2081bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
2082bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
208315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
20845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDb(uri)) {
2085072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro            if (applyingBatch()) {
2086072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                switchToProfileMode();
2087072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                return mProfileProvider.update(uri, values, selection, selectionArgs);
2088072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro            } else {
2089072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                // Start a contacts DB transaction to maintain provider synchronization.
2090072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                SQLiteDatabase contactsDb = mContactsHelper.getWritableDatabase();
2091072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                contactsDb.beginTransactionWithListener(this);
2092072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                int result = 0;
2093072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                try {
2094072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    // Now switch to profile mode and proceed with the update using its provider.
2095072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    switchToProfileMode();
2096072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    result = mProfileProvider.update(uri, values, selection, selectionArgs);
2097072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro
2098072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    contactsDb.setTransactionSuccessful();
2099072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                } finally {
2100072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    // Finish the contacts transaction, allowing other provider operations to
2101072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    // proceed.
2102072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    contactsDb.endTransaction();
2103072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                }
2104072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                return result;
2105072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro            }
21065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } else {
21075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToContactMode();
21085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return super.update(uri, values, selection, selectionArgs);
21095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
2110568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2111568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2112568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2113568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
211415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
21155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDb(uri)) {
2116072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro            if (applyingBatch()) {
2117072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                switchToProfileMode();
2118072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                return mProfileProvider.delete(uri, selection, selectionArgs);
2119072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro            } else {
2120072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                // Start a contacts DB transaction to maintain provider synchronization.
2121072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                SQLiteDatabase contactsDb = mContactsHelper.getWritableDatabase();
2122072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                contactsDb.beginTransactionWithListener(this);
2123072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                int result = 0;
2124072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                try {
2125072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    // Now switch to profile mode and proceed with the delete using its provider.
2126072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    switchToProfileMode();
2127072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    result = mProfileProvider.delete(uri, selection, selectionArgs);
2128072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro
2129072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    contactsDb.setTransactionSuccessful();
2130072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                } finally {
2131072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    // Finish the contacts transaction, allowing other provider operations to
2132072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    // proceed.
2133072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    contactsDb.endTransaction();
2134072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                }
2135072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                return result;
2136072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro            }
21375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } else {
21385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToContactMode();
21395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return super.delete(uri, selection, selectionArgs);
21405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
21415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
21425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
21435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
21445d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Replaces the current (thread-local) database to use for the operation with the given one.
21455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @param db The database to use.
21465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
21475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /* package */ void substituteDb(SQLiteDatabase db) {
21485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.set(db);
2149568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2150568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2151568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2152568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
2153568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            throws OperationApplicationException {
215415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
21555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        ContentProviderResult[] results = null;
21565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        try {
2157d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            mApplyingBatch.set(true);
21585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            results = super.applyBatch(operations);
21595d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } finally {
2160d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            mApplyingBatch.set(false);
21615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (mProfileDbForBatch.get() != null) {
21625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                // A profile operation was involved, so clean up its transaction.
21635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                boolean profileErrors = mProfileErrorsInBatch.get() != null
21645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        && mProfileErrorsInBatch.get();
21655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                if (!profileErrors) {
21665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mProfileDbForBatch.get().setTransactionSuccessful();
21675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                }
21685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mProfileDbForBatch.get().endTransaction();
21695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mProfileDbForBatch.set(null);
21705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mProfileErrorsInBatch.set(false);
21715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
21725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
21735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return results;
2174568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2175568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
21764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
21777b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    public int bulkInsert(Uri uri, ContentValues[] values) {
21787b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov        waitForAccess(mWriteAccessLatch);
2179720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro
2180720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro        // Note: This duplicates much of the logic in the superclass, but handles toggling
2181720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro        // into profile mode if necessary.
2182720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro        int numValues = values.length;
2183720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro        boolean notifyChange = false;
2184720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro        SQLiteDatabase profileDb = null;
2185072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro
2186072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro        // Always get a contacts DB and start a transaction on it, to maintain provider
2187072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro        // synchronization.
21888d55bbc171bea632e960e0e5a88557cef5e66d92Dave Santoro        mDb = mContactsHelper.getWritableDatabase();
21898d55bbc171bea632e960e0e5a88557cef5e66d92Dave Santoro        mDb.beginTransactionWithListener(this);
2190720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro        try {
2191720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro            for (int i = 0; i < numValues; i++) {
2192720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                Uri result;
2193720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                if (mapsToProfileDbWithInsertedValues(uri, values[i])) {
2194720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                    switchToProfileMode();
2195072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro
2196072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    // Initialize the profile DB and start a profile transaction if we haven't
2197072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    // already done so.
2198720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                    if (profileDb == null) {
2199720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                        profileDb = mProfileHelper.getWritableDatabase();
2200720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                        profileDb.beginTransactionWithListener(this);
2201720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                    }
2202720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                    result = mProfileProvider.insertInTransaction(uri, values[i]);
2203720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                } else {
2204720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                    switchToContactMode();
2205720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                    result = insertInTransaction(uri, values[i]);
2206720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                }
2207720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                if (result != null) {
2208720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                    notifyChange = true;
2209720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                }
2210720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                boolean savedNotifyChange = notifyChange;
2211720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                mActiveDb.get().yieldIfContendedSafely();
2212720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                notifyChange = savedNotifyChange;
2213720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro            }
22148d55bbc171bea632e960e0e5a88557cef5e66d92Dave Santoro            mDb.setTransactionSuccessful();
2215720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro            if (profileDb != null) {
2216720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                profileDb.setTransactionSuccessful();
2217720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro            }
2218720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro        } finally {
22198d55bbc171bea632e960e0e5a88557cef5e66d92Dave Santoro            mDb.endTransaction();
2220720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro            if (profileDb != null) {
2221720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                profileDb.endTransaction();
2222720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro            }
2223720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro        }
2224720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro
2225720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro        if (notifyChange) {
2226720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro            notifyChange();
2227720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro        }
2228720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro        return numValues;
22297b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    }
22307b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov
22317b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    @Override
2232285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void onBeginTransaction() {
2233bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2234b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "onBeginTransaction");
2235b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2236285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.onBeginTransaction();
22375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (inProfileMode()) {
22385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mProfileAggregator.clearPendingAggregations();
22395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mProfileTransactionContext.clear();
22405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } else {
22415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mContactAggregator.clearPendingAggregations();
22425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mContactTransactionContext.clear();
22435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
2244b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2245b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2246285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2247285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2248285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void beforeTransactionCommit() {
22491129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
2250bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2251b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "beforeTransactionCommit");
2252b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2253285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.beforeTransactionCommit();
2254b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
22555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().aggregateInTransaction(mTransactionContext.get(), mActiveDb.get());
22561a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (mVisibleTouched) {
22571a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = false;
22585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mDbHelper.get().updateAllVisible();
22591a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        }
22603826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
2261bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        updateSearchIndexInTransaction();
2262bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov
22633826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatusUpdateNeeded) {
22643826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
22653826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatusUpdateNeeded = false;
22663826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
2267b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2268b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2269bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov    private void updateSearchIndexInTransaction() {
22705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> staleContacts = mTransactionContext.get().getStaleSearchIndexContactIds();
22715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> staleRawContacts = mTransactionContext.get().getStaleSearchIndexRawContactIds();
2272bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        if (!staleContacts.isEmpty() || !staleRawContacts.isEmpty()) {
2273bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mSearchIndexManager.updateIndexForRawContacts(staleContacts, staleRawContacts);
22745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mTransactionContext.get().clearSearchIndexUpdates();
2275bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        }
2276bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov    }
2277bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov
2278b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void flushTransactionalChanges() {
2279bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2280b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "flushTransactionChanges");
2281b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
22821129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
22835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        for (long rawContactId : mTransactionContext.get().getInsertedRawContactIds()) {
22845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mDbHelper.get().updateRawContactDisplayName(mActiveDb.get(), rawContactId);
22855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mAggregator.get().onRawContactInsert(mTransactionContext.get(), mActiveDb.get(),
22865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    rawContactId);
228724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
228824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
22895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> dirtyRawContacts = mTransactionContext.get().getDirtyRawContactIds();
2290d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!dirtyRawContacts.isEmpty()) {
2291a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
2292a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_DIRTY_SQL);
2293d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, dirtyRawContacts);
2294a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
22955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().execSQL(mSb.toString());
2296a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        }
2297a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
22985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> updatedRawContacts = mTransactionContext.get().getUpdatedRawContactIds();
2299d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!updatedRawContacts.isEmpty()) {
2300a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
2301a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_VERSION_SQL);
2302d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, updatedRawContacts);
2303a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
23045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().execSQL(mSb.toString());
2305b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2306b5a4add17815167d20a90645779df34cdf45280dFred Quintana
23075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Update sync states.
23085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        for (Map.Entry<Long, Object> entry : mTransactionContext.get().getUpdatedSyncStates()) {
2309b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long id = entry.getKey();
23105d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (mDbHelper.get().getSyncState().update(mActiveDb.get(), id, entry.getValue()) <= 0) {
23119d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                throw new IllegalStateException(
23129d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                        "unable to update sync state, does it still exist?");
23139d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            }
2314b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2315b5a4add17815167d20a90645779df34cdf45280dFred Quintana
23165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mTransactionContext.get().clear();
2317b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2318b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2319a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /**
2320a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * Appends comma separated ids.
2321a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * @param ids Should not be empty
2322a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     */
2323d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov    private void appendIds(StringBuilder sb, Set<Long> ids) {
2324b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long id : ids) {
2325a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            sb.append(id).append(',');
2326b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2327a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
2328a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        sb.setLength(sb.length() - 1); // Yank the last comma
2329285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
2330285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2331285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2332cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    protected void notifyChange() {
233381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        notifyChange(mSyncToNetwork);
233481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = false;
233581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
233681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
233781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    protected void notifyChange(boolean syncToNetwork) {
233881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
233981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                syncToNetwork);
2340cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    }
2341568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
234251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    protected void setProviderStatus(int status) {
23433826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != status) {
23443826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatus = status;
23453826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            getContext().getContentResolver().notifyChange(ProviderStatus.CONTENT_URI, null, false);
23463826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
234751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
234851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
2349f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    public DataRowHandler getDataRowHandler(final String mimeType) {
23505d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (inProfileMode()) {
23515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return getDataRowHandlerForProfile(mimeType);
23525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
23533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
23543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
23556d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov            handler = new DataRowHandlerForCustomMimetype(
23565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    getContext(), mContactsHelper, mContactAggregator, mimeType);
23573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
23583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
23593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
23603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
23613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
23625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    public DataRowHandler getDataRowHandlerForProfile(final String mimeType) {
23635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        DataRowHandler handler = mProfileDataRowHandlers.get(mimeType);
23645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (handler == null) {
23655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            handler = new DataRowHandlerForCustomMimetype(
23665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    getContext(), mProfileHelper, mProfileAggregator, mimeType);
23675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mProfileDataRowHandlers.put(mimeType, handler);
23685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
23695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return handler;
23705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
23715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
23724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2373de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected Uri insertInTransaction(Uri uri, ContentValues values) {
2374bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
23751129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Log.v(TAG, "insertInTransaction: " + uri + " " + values);
2376b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2377f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
23785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
23795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
23805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.set(mDb);
23815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
23825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
2383f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2384f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2385f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2386a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
2387a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
238835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2389a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
239035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
23915d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE:
23925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                id = mDbHelper.get().getSyncState().insert(mActiveDb.get(), values);
239335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
239435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2395d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
2396d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                insertContact(values);
23976bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
23986bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
23996bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
240024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
240124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                throw new UnsupportedOperationException(
240224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        "The profile contact is created automatically");
240324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
240424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2405d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS:
2406d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS: {
24075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                id = insertRawContact(uri, values, callerIsSyncAdapter);
2408f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2409a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2410a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2411a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2412d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS_DATA:
2413d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID_DATA: {
2414d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                int segment = match == RAW_CONTACTS_DATA ? 1 : 2;
2415d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(segment));
2416f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2417f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2418a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2419a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2420a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
24213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case RAW_CONTACTS_ID_STREAM_ITEMS: {
24223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                values.put(StreamItems.RAW_CONTACT_ID, uri.getPathSegments().get(1));
24233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItem(uri, values);
24243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
24253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
24263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
24273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24280c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case DATA:
24290c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case PROFILE_DATA: {
2430f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2431f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2432a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2433a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2434a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2435ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
2436f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                id = insertGroup(uri, values, callerIsSyncAdapter);
2437f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2438ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2439ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2440ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2441eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
24425aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertSettings(uri, values);
244343880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2444eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
2445eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2446eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
24475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case STATUS_UPDATES:
24485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_STATUS_UPDATES: {
244982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                id = insertStatusUpdate(values);
24501f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
24511f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
24521f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
24533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
24543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItem(uri, values);
24553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
24563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
24573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
24583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
24603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItemPhoto(uri, values);
24613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
24623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
24633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
24643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
24663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                values.put(StreamItemPhotos.STREAM_ITEM_ID, uri.getPathSegments().get(1));
24673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItemPhoto(uri, values);
24683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
24693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
24703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
24713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
2472a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
247381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
2474f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
2475a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2476a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
24777e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
24787e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
24797e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
24807e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2481de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        return ContentUris.withAppendedId(uri, id);
2482a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2483a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2484a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2485e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * If account is non-null then store it in the values. If the account is
2486e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * already specified in the values then it must be consistent with the
2487e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * account, if it is non-null.
2488e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *
2489e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param uri Current {@link Uri} being operated on.
2490e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param values {@link ContentValues} to read and possibly update.
2491e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when only one of
2492e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_NAME} or
2493e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_TYPE} is specified, leaving the
2494e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             other undefined.
2495e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when {@link RawContacts#ACCOUNT_NAME}
2496e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             and {@link RawContacts#ACCOUNT_TYPE} are inconsistent between
2497e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             the given {@link Uri} and {@link ContentValues}.
24987e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
2499e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey    private Account resolveAccount(Uri uri, ContentValues values) throws IllegalArgumentException {
2500f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
2501f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
2502e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
2503f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2504f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
2505f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
2506e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialValues = TextUtils.isEmpty(valueAccountName)
2507e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                ^ TextUtils.isEmpty(valueAccountType);
2508e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2509e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri || partialValues) {
2510e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
25115d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
2512fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
2513e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
2514e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2515e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
2516e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
2517e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validUri = !TextUtils.isEmpty(accountName);
2518e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validValues = !TextUtils.isEmpty(valueAccountName);
2519e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2520e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validValues && validUri) {
2521e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Check that accounts match when both present
2522e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            final boolean accountMatch = TextUtils.equals(accountName, valueAccountName)
2523e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    && TextUtils.equals(accountType, valueAccountType);
2524e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            if (!accountMatch) {
25255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
2526fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        "When both specified, ACCOUNT_NAME and ACCOUNT_TYPE must match", uri));
2527e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            }
2528e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validUri) {
2529e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Fill values from Uri when not present
2530f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_NAME, accountName);
2531f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_TYPE, accountType);
2532e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validValues) {
2533f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountName = valueAccountName;
2534f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountType = valueAccountType;
2535e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else {
2536e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            return null;
2537f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
2538f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2539e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Use cached Account object when matches, otherwise create
2540f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mAccount == null
2541f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.name.equals(accountName)
2542f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.type.equals(accountType)) {
2543f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mAccount = new Account(accountName, accountType);
2544035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
2545f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2546e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        return mAccount;
25477e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
25487e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
25497e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
255043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * Resolves the account and builds an {@link AccountWithDataSet} based on the data set specified
255143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * in the URI or values (if any).
255243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * @param uri Current {@link Uri} being operated on.
255343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * @param values {@link ContentValues} to read and possibly update.
255443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     */
255543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    private AccountWithDataSet resolveAccountWithDataSet(Uri uri, ContentValues values) {
25563593682b8d9213fde576a0cff54458ad50563980Dave Santoro        final Account account = resolveAccount(uri, values);
255743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        AccountWithDataSet accountWithDataSet = null;
255843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        if (account != null) {
255943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            String dataSet = getQueryParameter(uri, RawContacts.DATA_SET);
256043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            if (dataSet == null) {
25613593682b8d9213fde576a0cff54458ad50563980Dave Santoro                dataSet = values.getAsString(RawContacts.DATA_SET);
2562a71dc460ca951c7aca591f3f470c160cde70a1e3Dave Santoro            } else {
25633593682b8d9213fde576a0cff54458ad50563980Dave Santoro                values.put(RawContacts.DATA_SET, dataSet);
256443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
256543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            accountWithDataSet = new AccountWithDataSet(account.name, account.type, dataSet);
256643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        }
256743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        return accountWithDataSet;
256843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    }
256943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
257043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    /**
2571d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Inserts an item in the contacts table
25726bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
25736bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
25746bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
25756bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
2576d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertContact(ContentValues values) {
2577de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregate contacts are created automatically");
25786bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
25796bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
25806bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
258124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Inserts an item in the raw contacts table
2582a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2583f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param uri the values for the new row
2584f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param values the account this contact should be associated with. may be null.
2585dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana     * @param callerIsSyncAdapter
2586a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2587a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
25885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private long insertRawContact(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
2589f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2590f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2591f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2592f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
259343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        AccountWithDataSet accountWithDataSet = resolveAccountWithDataSet(uri, mValues);
25947e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
25953d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
25963d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
2597f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
25983d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
25993d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
26005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        long rawContactId = mActiveDb.get().insert(Tables.RAW_CONTACTS,
26015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                RawContacts.CONTACT_ID, mValues);
2602f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        int aggregationMode = RawContacts.AGGREGATION_MODE_DEFAULT;
26035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mValues.containsKey(RawContacts.AGGREGATION_MODE)) {
2604f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            aggregationMode = mValues.getAsInteger(RawContacts.AGGREGATION_MODE);
2605f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        }
26065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().markNewForAggregation(rawContactId, aggregationMode);
2607285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
26085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Trigger creation of a Contact based on this RawContact at the end of transaction
26095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mTransactionContext.get().rawContactInserted(rawContactId, accountWithDataSet);
2610f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2611dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter) {
2612dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            addAutoAddMembership(rawContactId);
2613dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            final Long starred = values.getAsLong(RawContacts.STARRED);
2614dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (starred != null && starred != 0) {
2615dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateFavoritesMembership(rawContactId, starred != 0);
2616dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2617dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2618dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
26193826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
2620023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        return rawContactId;
2621a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2622a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2623dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void addAutoAddMembership(long rawContactId) {
2624dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID,
2625dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2626dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2627dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            insertDataGroupMembership(rawContactId, groupId);
2628dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2629dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2630dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2631dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private Long findGroupByRawContactId(String selection, long rawContactId) {
26325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = mActiveDb.get().query(Tables.GROUPS + "," + Tables.RAW_CONTACTS,
26335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                PROJECTION_GROUP_ID, selection,
2634dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                new String[]{Long.toString(rawContactId)},
2635dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                null /* groupBy */, null /* having */, null /* orderBy */);
2636dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        try {
2637dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            while (c.moveToNext()) {
2638dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return c.getLong(0);
2639dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2640dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return null;
2641dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        } finally {
2642dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            c.close();
2643dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2644dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2645dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2646dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void updateFavoritesMembership(long rawContactId, boolean isStarred) {
2647dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID,
2648dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2649dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2650dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (isStarred) {
2651dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                insertDataGroupMembership(rawContactId, groupId);
2652dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
2653dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                deleteDataGroupMembership(rawContactId, groupId);
2654dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2655dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2656dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2657dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2658dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void insertDataGroupMembership(long rawContactId, long groupId) {
2659dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        ContentValues groupMembershipValues = new ContentValues();
2660dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.GROUP_ROW_ID, groupId);
2661dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.RAW_CONTACT_ID, rawContactId);
2662dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(DataColumns.MIMETYPE_ID,
26635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mDbHelper.get().getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
26645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.get().insert(Tables.DATA, null, groupMembershipValues);
2665dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2666dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2667dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void deleteDataGroupMembership(long rawContactId, long groupId) {
2668dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final String[] selectionArgs = {
26695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Long.toString(mDbHelper.get().getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE)),
2670dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(groupId),
2671dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(rawContactId)};
26725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.get().delete(Tables.DATA, SELECTION_GROUPMEMBERSHIP_DATA, selectionArgs);
2673dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2674dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2675a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2676a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
2677a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2678a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
2679a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2680a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2681f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
2682a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
2683de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.clear();
2684de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.putAll(values);
268567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
2686de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
268720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2688de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace package with internal mapping
2689de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
2690de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (packageName != null) {
26915d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.get().getPackageId(packageName));
2692de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
2693de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.RES_PACKAGE);
2694508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2695de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace mimetype with internal mapping
2696de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String mimeType = mValues.getAsString(Data.MIMETYPE);
2697de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (TextUtils.isEmpty(mimeType)) {
2698de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
2699de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
27004097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
27015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.get().getMimeTypeId(mimeType));
2702de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
2703a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2704a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
27055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        id = rowHandler.insert(mActiveDb.get(), mTransactionContext.get(), rawContactId, mValues);
2706f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
27075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mTransactionContext.get().markRawContactDirty(rawContactId);
2708a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
27095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mTransactionContext.get().rawContactUpdated(rawContactId);
2710a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
27114f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
27124f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
27133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
27143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Inserts an item in the stream_items table.  The account is checked against the
27153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account in the raw contact for which the stream item is being inserted.  If the
27163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * new stream item results in more stream items under this raw contact than the limit,
27173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the oldest one will be deleted (note that if the stream item inserted was the
27183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * oldest, it will be immediately deleted, and this will return 0).
27193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
27203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param uri the insertion URI
27213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param values the values for the new row
27223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return the stream item _ID of the newly created row, or 0 if it was not created
27233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
27243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long insertStreamItem(Uri uri, ContentValues values) {
27253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long id = 0;
27263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.clear();
27273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.putAll(values);
27283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long rawContactId = mValues.getAsLong(StreamItems.RAW_CONTACT_ID);
27303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Ensure that the raw contact exists and belongs to the caller's account.
27323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, mValues);
27333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccount(account, rawContactId);
27343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27356802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to insert accounts params - they don't exist in the stream items table.
27366802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        mValues.remove(RawContacts.ACCOUNT_NAME);
27376802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        mValues.remove(RawContacts.ACCOUNT_TYPE);
27386802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
27393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Insert the new stream item.
27405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        id = mActiveDb.get().insert(Tables.STREAM_ITEMS, null, mValues);
27416802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (id == -1) {
27426802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Insertion failed.
27436802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return 0;
27446802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
27453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check to see if we're over the limit for stream items under this raw contact.
27473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // It's possible that the inserted stream item is older than the the existing
27483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // ones, in which case it may be deleted immediately (resetting the ID to 0).
27493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        id = cleanUpOldStreamItems(rawContactId, id);
27503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return id;
27523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
27533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
27553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Inserts an item in the stream_item_photos table.  The account is checked against
27563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the account in the raw contact that owns the stream item being modified.
27573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
27583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param uri the insertion URI
27593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param values the values for the new row
27606802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @return the stream item photo _ID of the newly created row, or 0 if there was an issue
27616802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     *     with processing the photo or creating the row
27623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
27633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long insertStreamItemPhoto(Uri uri, ContentValues values) {
27643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long id = 0;
27653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.clear();
27663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.putAll(values);
27673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long streamItemId = mValues.getAsLong(StreamItemPhotos.STREAM_ITEM_ID);
27693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        if (streamItemId != 0) {
27703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            long rawContactId = lookupRawContactIdForStreamId(streamItemId);
27713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            // Ensure that the raw contact exists and belongs to the caller's account.
27733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            Account account = resolveAccount(uri, mValues);
27743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            enforceModifyingAccount(account, rawContactId);
27753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27766802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Don't attempt to insert accounts params - they don't exist in the stream item
27776802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // photos table.
27786802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            mValues.remove(RawContacts.ACCOUNT_NAME);
27796802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            mValues.remove(RawContacts.ACCOUNT_TYPE);
27803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27816802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Process the photo and store it.
27826802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            if (processStreamItemPhoto(mValues, false)) {
27836802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // Insert the stream item photo.
27845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                id = mActiveDb.get().insert(Tables.STREAM_ITEM_PHOTOS, null, mValues);
27856802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
27863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
27873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return id;
27883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
27893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
27916802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * Processes the photo contained in the {@link ContactsContract.StreamItemPhotos#PHOTO}
27926802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * field of the given values, attempting to store it in the photo store.  If successful,
27936802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * the resulting photo file ID will be added to the values for insert/update in the table.
27946802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * <p>
27956802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * If updating, it is valid for the picture to be empty or unspecified (the function will
27966802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * still return true).  If inserting, a valid picture must be specified.
27976802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @param values The content values provided by the caller.
27986802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @param forUpdate Whether this photo is being processed for update (vs. insert).
27996802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @return Whether the insert or update should proceed.
28006802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     */
28016802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    private boolean processStreamItemPhoto(ContentValues values, boolean forUpdate) {
28026802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (!values.containsKey(StreamItemPhotos.PHOTO)) {
28036802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return forUpdate;
28046802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
28056802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        byte[] photoBytes = values.getAsByteArray(StreamItemPhotos.PHOTO);
28066802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (photoBytes == null) {
28076802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return forUpdate;
28086802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
28096802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
28106802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Process the photo and store it.
28116802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        try {
28125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            long photoFileId = mPhotoStore.get().insert(new PhotoProcessor(photoBytes,
28131dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                    mMaxDisplayPhotoDim, mMaxThumbnailPhotoDim, true), true);
28146802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            if (photoFileId != 0) {
28156802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                values.put(StreamItemPhotos.PHOTO_FILE_ID, photoFileId);
28166802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                values.remove(StreamItemPhotos.PHOTO);
28176802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                return true;
28186802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            } else {
28196802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // Couldn't store the photo, return 0.
28206802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                Log.e(TAG, "Could not process stream item photo for insert");
28216802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                return false;
28226802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
28236802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        } catch (IOException ioe) {
28246802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            Log.e(TAG, "Could not process stream item photo for insert", ioe);
28256802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return false;
28266802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
28276802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    }
28286802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
28296802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    /**
28303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Looks up the raw contact ID that owns the specified stream item.
28313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param streamItemId The ID of the stream item.
28323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The associated raw contact ID, or -1 if no such stream item exists.
28333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
28343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long lookupRawContactIdForStreamId(long streamItemId) {
28353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long rawContactId = -1;
28365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = mActiveDb.get().query(Tables.STREAM_ITEMS,
28375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new String[]{StreamItems.RAW_CONTACT_ID},
28383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                StreamItems._ID + "=?", new String[]{String.valueOf(streamItemId)},
28393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                null, null, null);
28403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
28413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if (c.moveToFirst()) {
28423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                rawContactId = c.getLong(0);
28433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
28443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
28453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
28463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
28473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return rawContactId;
28483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
28493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
28503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
28513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given raw contact ID is owned by the given account.
28523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * If the resolved account is null, this will return true iff the raw contact
28533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * is also associated with the "null" account.
28543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
28553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * If the resolved account does not match, this will throw a security exception.
28563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
28573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param rawContactId The raw contact ID to check for.
28583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
28593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void enforceModifyingAccount(Account account, long rawContactId) {
28603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        String accountSelection = RawContactsColumns.CONCRETE_ID + "=? AND "
28613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_NAME + "=? AND "
28623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + "=?";
28633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        String noAccountSelection = RawContactsColumns.CONCRETE_ID + "=? AND "
28643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " IS NULL AND "
28653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " IS NULL";
28663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c;
28673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        if (account != null) {
28685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            c = mActiveDb.get().query(Tables.RAW_CONTACTS,
28695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    new String[]{RawContactsColumns.CONCRETE_ID}, accountSelection,
28703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    new String[]{String.valueOf(rawContactId), mAccount.name, mAccount.type},
28713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    null, null, null);
28723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } else {
28735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            c = mActiveDb.get().query(Tables.RAW_CONTACTS,
28745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    new String[]{RawContactsColumns.CONCRETE_ID}, noAccountSelection,
28755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    new String[]{String.valueOf(rawContactId)},
28763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    null, null, null);
28773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
28783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
28793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if(c.getCount() == 0) {
28803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                throw new SecurityException("Caller account does not match raw contact ID "
28813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    + rawContactId);
28823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
28833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
28843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
28853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
28863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
28873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
28883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
28893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given selection of stream items matches up with the given
28903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account.  If any of the raw contacts fail the account check, this will throw a
28913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * security exception.
28923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
28933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selection The selection.
28943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selectionArgs The selection arguments.
28953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The list of stream item IDs that would be included in this selection.
28963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
28973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private List<Long> enforceModifyingAccountForStreamItems(Account account, String selection,
28983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
28993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemIds = Lists.newArrayList();
29003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
29013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        setTablesAndProjectionMapForStreamItems(qb);
29025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = qb.query(mActiveDb.get(),
29033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{StreamItems._ID, StreamItems.RAW_CONTACT_ID},
29043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selection, selectionArgs, null, null, null);
29053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
29063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            while (c.moveToNext()) {
29073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                streamItemIds.add(c.getLong(0));
29083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Throw a security exception if the account doesn't match the raw contact's.
29103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                enforceModifyingAccount(account, c.getLong(1));
29113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
29123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
29133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
29143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
29153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemIds;
29163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
29173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
29193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given selection of stream item photos matches up with the given
29203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account.  If any of the raw contacts fail the account check, this will throw a
29213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * security exception.
29223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
29233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selection The selection.
29243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selectionArgs The selection arguments.
29253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The list of stream item photo IDs that would be included in this selection.
29263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
29273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private List<Long> enforceModifyingAccountForStreamItemPhotos(Account account, String selection,
29283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
29293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemPhotoIds = Lists.newArrayList();
29303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
29313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        setTablesAndProjectionMapForStreamItemPhotos(qb);
29325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = qb.query(mActiveDb.get(),
29335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new String[]{StreamItemPhotos._ID, StreamItems.RAW_CONTACT_ID},
29343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selection, selectionArgs, null, null, null);
29353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
29363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            while (c.moveToNext()) {
29373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                streamItemPhotoIds.add(c.getLong(0));
29383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Throw a security exception if the account doesn't match the raw contact's.
29403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                enforceModifyingAccount(account, c.getLong(1));
29413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
29423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
29433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
29443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
29453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemPhotoIds;
29463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
29473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
29493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Queries the database for stream items under the given raw contact.  If there are
29503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * more entries than {@link ContactsProvider2#MAX_STREAM_ITEMS_PER_RAW_CONTACT},
29513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the oldest entries (as determined by timestamp) will be deleted.
29523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param rawContactId The raw contact ID to examine for stream items.
29533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param insertedStreamItemId The ID of the stream item that was just inserted,
29543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     prompting this cleanup.  Callers may pass 0 if no insertion prompted the
29553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     cleanup.
29563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The ID of the inserted stream item if it still exists after cleanup;
29573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     0 otherwise.
29583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
29593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long cleanUpOldStreamItems(long rawContactId, long insertedStreamItemId) {
29603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long postCleanupInsertedStreamId = insertedStreamItemId;
29615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = mActiveDb.get().query(Tables.STREAM_ITEMS, new String[]{StreamItems._ID},
29623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                StreamItems.RAW_CONTACT_ID + "=?", new String[]{String.valueOf(rawContactId)},
29633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                null, null, StreamItems.TIMESTAMP + " DESC, " + StreamItems._ID + " DESC");
29643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
29653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            int streamItemCount = c.getCount();
29663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if (streamItemCount <= MAX_STREAM_ITEMS_PER_RAW_CONTACT) {
29673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Still under the limit - nothing to clean up!
29683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return insertedStreamItemId;
29693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            } else {
29703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                c.moveToLast();
29713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                while (c.getPosition() >= MAX_STREAM_ITEMS_PER_RAW_CONTACT) {
29723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    long streamItemId = c.getLong(0);
29733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    if (insertedStreamItemId == streamItemId) {
29743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        // The stream item just inserted is being deleted.
29753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        postCleanupInsertedStreamId = 0;
29763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    }
29773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    deleteStreamItem(c.getLong(0));
29783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    c.moveToPrevious();
29793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
29803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
29813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
29823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
29833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
29843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return postCleanupInsertedStreamId;
29853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
29863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29879261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
298820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
298920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
2990f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
299120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
299220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2993de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
2994de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
29950c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro        Uri dataUri = inProfileMode()
29960c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro                ? Uri.withAppendedPath(Profile.CONTENT_URI, RawContacts.Data.CONTENT_DIRECTORY)
29970c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro                : Data.CONTENT_URI;
29980c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro        Cursor c = query(dataUri, DataRowHandler.DataDeleteQuery.COLUMNS,
2999f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                selection, selectionArgs, null);
3000de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        try {
3001de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            while(c.moveToNext()) {
3002f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                long rawContactId = c.getLong(DataRowHandler.DataDeleteQuery.RAW_CONTACT_ID);
3003f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
3004a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                DataRowHandler rowHandler = getDataRowHandler(mimeType);
30055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                count += rowHandler.delete(mActiveDb.get(), mTransactionContext.get(), c);
3006f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                if (!callerIsSyncAdapter) {
30075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mTransactionContext.get().markRawContactDirty(rawContactId);
300888e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                }
300920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
301020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
3011de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            c.close();
301220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
301320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
301420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
301520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
301620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
301788e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    /**
301888e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     * Delete a data row provided that it is one of the allowed mime types.
301988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     */
302020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
3021f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
302288e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
302388e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
30244da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(dataId);
3025f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataRowHandler.DataDeleteQuery.COLUMNS, Data._ID + "=?",
30264da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1, null);
3027f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
302820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
302920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
303020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
303120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
303220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3033f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov            String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
303420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
303520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
303620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
303720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
303820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
303920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
304020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
304120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
304220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
30437a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException("Data type mismatch: expected "
304420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
304520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3046a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            DataRowHandler rowHandler = getDataRowHandler(mimeType);
30475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return rowHandler.delete(mActiveDb.get(), mTransactionContext.get(), c);
304820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
304920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
305020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
305120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
305220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
305320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
3054ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
3055ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
3056f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private long insertGroup(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
3057f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
3058f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
3059f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
30603593682b8d9213fde576a0cff54458ad50563980Dave Santoro        final AccountWithDataSet accountWithDataSet = resolveAccountWithDataSet(uri, mValues);
3061ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3062ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
3063f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String packageName = mValues.getAsString(Groups.RES_PACKAGE);
306467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
30655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mValues.put(GroupsColumns.PACKAGE_ID, mDbHelper.get().getPackageId(packageName));
306667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
3067f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.remove(Groups.RES_PACKAGE);
3068ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3069dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final boolean isFavoritesGroup = mValues.getAsLong(Groups.FAVORITES) != null
3070dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                ? mValues.getAsLong(Groups.FAVORITES) != 0
3071dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                : false;
3072dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3073f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
3074f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(Groups.DIRTY, 1);
307573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
307673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
30775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        long result = mActiveDb.get().insert(Tables.GROUPS, Groups.TITLE, mValues);
3078ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
3079dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter && isFavoritesGroup) {
3080dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // add all starred raw contacts to this group
3081dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String selection;
3082dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs;
30833593682b8d9213fde576a0cff54458ad50563980Dave Santoro            if (accountWithDataSet == null) {
3084dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + " IS NULL AND "
308543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + RawContacts.ACCOUNT_TYPE + " IS NULL AND "
308643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + RawContacts.DATA_SET + " IS NULL";
3087dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = null;
30883593682b8d9213fde576a0cff54458ad50563980Dave Santoro            } else if (accountWithDataSet.getDataSet() == null) {
3089dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + "=? AND "
30903593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        + RawContacts.ACCOUNT_TYPE + "=? AND "
30913593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        + RawContacts.DATA_SET + " IS NULL";
30923593682b8d9213fde576a0cff54458ad50563980Dave Santoro                selectionArgs = new String[] {
30933593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getAccountName(),
30943593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getAccountType()
30953593682b8d9213fde576a0cff54458ad50563980Dave Santoro                };
309643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            } else {
309743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                selection = RawContacts.ACCOUNT_NAME + "=? AND "
309843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + RawContacts.ACCOUNT_TYPE + "=? AND "
309943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + RawContacts.DATA_SET + "=?";
31003593682b8d9213fde576a0cff54458ad50563980Dave Santoro                selectionArgs = new String[] {
31013593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getAccountName(),
31023593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getAccountType(),
31033593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getDataSet()
31043593682b8d9213fde576a0cff54458ad50563980Dave Santoro                };
3105dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
31065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS,
3107dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[]{RawContacts._ID, RawContacts.STARRED},
3108dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    selection, selectionArgs, null, null, null);
3109892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            try {
3110892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                while (c.moveToNext()) {
3111892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (c.getLong(1) != 0) {
3112892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        final long rawContactId = c.getLong(0);
3113892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        insertDataGroupMembership(rawContactId, result);
31145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mTransactionContext.get().markRawContactDirty(rawContactId);
3115892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    }
3116dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
3117892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            } finally {
3118892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                c.close();
3119dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
3120dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
3121dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3122f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mValues.containsKey(Groups.GROUP_VISIBLE)) {
31231a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3124ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        }
3125ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
3126ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        return result;
3127ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
3128ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
31295aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private long insertSettings(Uri uri, ContentValues values) {
3130f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        // Before inserting, ensure that no settings record already exists for the
3131f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        // values being inserted (this used to be enforced by a primary key, but that no
3132f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        // longer works with the nullable data_set field added).
3133f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        String accountName = values.getAsString(Settings.ACCOUNT_NAME);
3134f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        String accountType = values.getAsString(Settings.ACCOUNT_TYPE);
3135f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        String dataSet = values.getAsString(Settings.DATA_SET);
3136f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        Uri.Builder settingsUri = Settings.CONTENT_URI.buildUpon();
3137f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        if (accountName != null) {
3138f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            settingsUri.appendQueryParameter(Settings.ACCOUNT_NAME, accountName);
3139f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        }
3140f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        if (accountType != null) {
3141f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            settingsUri.appendQueryParameter(Settings.ACCOUNT_TYPE, accountType);
3142f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        }
3143f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        if (dataSet != null) {
3144f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            settingsUri.appendQueryParameter(Settings.DATA_SET, dataSet);
3145f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        }
3146f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        Cursor c = queryLocal(settingsUri.build(), null, null, null, null, 0);
3147f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        try {
3148f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            if (c.getCount() > 0) {
3149f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                throw new SQLiteConstraintException("Can't insert a settings record with the same "
3150f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                        + "account name/type/data set");
3151f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            }
3152f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        } finally {
3153f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            c.close();
3154f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        }
3155f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro
3156f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        // If we didn't find a duplicate, we're fine to insert.
31575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        final long id = mActiveDb.get().insert(Tables.SETTINGS, null, values);
31585aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
31591a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
31601a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3161e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
31621a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
3163e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return id;
3164e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3165e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3166ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
316782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov     * Inserts a status update.
31681f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
316982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    public long insertStatusUpdate(ContentValues values) {
317082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        final String handle = values.getAsString(StatusUpdates.IM_HANDLE);
31710a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        final Integer protocol = values.getAsInteger(StatusUpdates.PROTOCOL);
31724dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        String customProtocol = null;
31734dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
31740a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        if (protocol != null && protocol == Im.PROTOCOL_CUSTOM) {
317582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            customProtocol = values.getAsString(StatusUpdates.CUSTOM_PROTOCOL);
31764dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (TextUtils.isEmpty(customProtocol)) {
31774dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                throw new IllegalArgumentException(
31784dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                        "CUSTOM_PROTOCOL is required when PROTOCOL=PROTOCOL_CUSTOM");
31794dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
31801f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
31811f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3182dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long rawContactId = -1;
3183dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long contactId = -1;
318482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Long dataId = values.getAsLong(StatusUpdates.DATA_ID);
31856802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        String accountType = null;
31866802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        String accountName = null;
3187f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.setLength(0);
31882526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov        mSelectionArgs.clear();
3189dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        if (dataId != null) {
3190dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the contact info for the given data row.
3191dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
31922526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSb.append(Tables.DATA + "." + Data._ID + "=?");
31932526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSelectionArgs.add(String.valueOf(dataId));
31941f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
3195dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the data row to attach this presence update to
3196dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
31970a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(handle) || protocol == null) {
31980a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                throw new IllegalArgumentException("PROTOCOL and IM_HANDLE are required");
31990a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
32000a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
3201dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // TODO: generalize to allow other providers to match against email
3202dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == protocol;
3203dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
32045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            String mimeTypeIdIm = String.valueOf(mDbHelper.get().getMimeTypeIdForIm());
3205dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            if (matchEmail) {
32065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                String mimeTypeIdEmail = String.valueOf(mDbHelper.get().getMimeTypeIdForEmail());
3207f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
3208f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // The following hack forces SQLite to use the (mimetype_id,data1) index, otherwise
3209f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the "OR" conjunction confuses it and it switches to a full scan of
3210f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the raw_contacts table.
3211f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
3212f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // This code relies on the fact that Im.DATA and Email.DATA are in fact the same
3213f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // column - Data.DATA1
32142526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + " IN (?,?)" +
32152526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Data.DATA1 + "=?" +
32162526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND ((" + DataColumns.MIMETYPE_ID + "=? AND " + Im.PROTOCOL + "=?");
32172526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
32182526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
32192526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
32202526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
32212526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
3222dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
32232526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
32242526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
3225dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
32262526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(") OR (" + DataColumns.MIMETYPE_ID + "=?))");
32272526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
3228dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            } else {
32292526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + "=?" +
32302526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.PROTOCOL + "=?" +
32312526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.DATA + "=?");
32322526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
32332526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
32342526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
3235dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
32362526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
32372526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
3238dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
3239dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
32401f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
324182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.DATA_ID)) {
32422526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(" AND " + DataColumns.CONCRETE_ID + "=?");
32432526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(values.getAsString(StatusUpdates.DATA_ID));
3244dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
324570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
324670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
32471f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
32481f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
32495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            cursor = mActiveDb.get().query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
32502526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.toString(), mSelectionArgs.toArray(EMPTY_STRING_ARRAY), null, null,
32514394086494fe7909aaca70f56fb4bb08beebf303Dmitri Plotnikov                    Clauses.CONTACT_VISIBLE + " DESC, " + Data.RAW_CONTACT_ID);
32521f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
325367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
32545ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
32556802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                accountType = cursor.getString(DataContactsQuery.ACCOUNT_TYPE);
32566802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                accountName = cursor.getString(DataContactsQuery.ACCOUNT_NAME);
3257e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
32581f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
32591f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
32601f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
32611f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
32621f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
326331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
326431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
326531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
32661f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
32671f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
326882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.PRESENCE)) {
3269a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (customProtocol == null) {
3270a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // We cannot allow a null in the custom protocol field, because SQLite3 does not
3271a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // properly enforce uniqueness of null values
3272a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                customProtocol = "";
3273a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
3274a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3275a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.clear();
327682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.DATA_ID, dataId);
3277a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.RAW_CONTACT_ID, rawContactId);
3278a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.CONTACT_ID, contactId);
327982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PROTOCOL, protocol);
328082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
328182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.IM_HANDLE, handle);
328282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.IM_ACCOUNT)) {
328382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                mValues.put(StatusUpdates.IM_ACCOUNT, values.getAsString(StatusUpdates.IM_ACCOUNT));
3284a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
328582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PRESENCE,
328682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    values.getAsString(StatusUpdates.PRESENCE));
3287aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori            mValues.put(StatusUpdates.CHAT_CAPABILITY,
3288aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                    values.getAsString(StatusUpdates.CHAT_CAPABILITY));
32891f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3290a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            // Insert the presence update
32915d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().replace(Tables.PRESENCE, null, mValues);
3292a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3293e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
32940a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
329582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.STATUS)) {
329682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String status = values.getAsString(StatusUpdates.STATUS);
32970a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String resPackage = values.getAsString(StatusUpdates.STATUS_RES_PACKAGE);
32980bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            Resources resources = getContext().getResources();
32990bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            if (!TextUtils.isEmpty(resPackage)) {
33000bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                PackageManager pm = getContext().getPackageManager();
33010bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                try {
33020bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                    resources = pm.getResourcesForApplication(resPackage);
33030bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                } catch (NameNotFoundException e) {
33040bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                    Log.w(TAG, "Contact status update resource package not found: "
33050bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                            + resPackage);
33060bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                }
33070bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            }
33080bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            Integer labelResourceId = values.getAsInteger(StatusUpdates.STATUS_LABEL);
33090a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
33100bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            if ((labelResourceId == null || labelResourceId == 0) && protocol != null) {
33110bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                labelResourceId = Im.getProtocolLabelResource(protocol);
33120a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
33130bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            String labelResource = getResourceName(resources, "string", labelResourceId);
33140a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
33150bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            Integer iconResourceId = values.getAsInteger(StatusUpdates.STATUS_ICON);
33160a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            // TODO compute the default icon based on the protocol
33170a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
33180bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            String iconResource = getResourceName(resources, "drawable", iconResourceId);
33190bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann
3320a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (TextUtils.isEmpty(status)) {
33215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mDbHelper.get().deleteStatusUpdate(dataId);
3322a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            } else {
33236802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                Long timestamp = values.getAsLong(StatusUpdates.STATUS_TIMESTAMP);
33246802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (timestamp != null) {
33255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mDbHelper.get().replaceStatusUpdate(dataId, timestamp, status, resPackage,
33260bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                            iconResourceId, labelResourceId);
33276802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                } else {
33285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mDbHelper.get().insertStatusUpdate(dataId, status, resPackage, iconResourceId,
33290bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                            labelResourceId);
33306802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                }
33316802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
33326802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // For forward compatibility with the new stream item API, insert this status update
33336802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // there as well.  If we already have a stream item from this source, update that
33346802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // one instead of inserting a new one (since the semantics of the old status update
33356802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // API is to only have a single record).
33366802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (rawContactId != -1 && !TextUtils.isEmpty(status)) {
33376802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    ContentValues streamItemValues = new ContentValues();
33386802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RAW_CONTACT_ID, rawContactId);
3339d5ef5903570e533a501abe6a8e3d533fdb5318fcFlavio Lerda                    // Status updates are text only but stream items are HTML.
3340e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda                    streamItemValues.put(StreamItems.TEXT, statusUpdateToHtml(status));
33416802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.COMMENTS, "");
33426802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_PACKAGE, resPackage);
33436802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_ICON, iconResource);
33446802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_LABEL, labelResource);
33456802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.TIMESTAMP,
33466802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            timestamp == null ? System.currentTimeMillis() : timestamp);
33476802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
33486802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // Note: The following is basically a workaround for the fact that status
33496802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // updates didn't do any sort of account enforcement, while social stream item
33506802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // updates do.  We can't expect callers of the old API to start passing account
33516802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // information along, so we just populate the account params appropriately for
335243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    // the raw contact.  Data set is not relevant here, as we only check account
335343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    // name and type.
33546802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    if (accountName != null && accountType != null) {
33556802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        streamItemValues.put(RawContacts.ACCOUNT_NAME, accountName);
33566802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        streamItemValues.put(RawContacts.ACCOUNT_TYPE, accountType);
33576802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    }
33586802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
33596802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // Check for an existing stream item from this source, and insert or update.
33606802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    Uri streamUri = StreamItems.CONTENT_URI;
33616802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    Cursor c = query(streamUri, new String[]{StreamItems._ID},
33626802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            StreamItems.RAW_CONTACT_ID + "=?",
33636802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            new String[]{String.valueOf(rawContactId)}, null);
33646802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    try {
33656802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        if (c.getCount() > 0) {
33666802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            c.moveToFirst();
33676802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            update(ContentUris.withAppendedId(streamUri, c.getLong(0)),
33686802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    streamItemValues, null, null);
33696802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        } else {
33706802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            insert(streamUri, streamItemValues);
33716802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        }
33726802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    } finally {
33736802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        c.close();
33746802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    }
33756802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                }
3376e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            }
3377e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        }
3378bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
3379a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (contactId != -1) {
33805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mAggregator.get().updateLastStatusUpdateId(contactId);
3381a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3382a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3383a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        return dataId;
33841f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
33851f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3386e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda    /** Converts a status update to HTML. */
3387e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda    private String statusUpdateToHtml(String status) {
33884747809486541f7a3d342d3e1dd48fb5ea255ad6Flavio Lerda        return TextUtils.htmlEncode(status);
3389e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda    }
3390e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda
33910bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann    private String getResourceName(Resources resources, String expectedType, Integer resourceId) {
33920bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann        try {
33930bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            if (resourceId == null || resourceId == 0) return null;
33940bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann
33950bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            // Resource has an invalid type (e.g. a string as icon)? ignore
33960bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            final String resourceEntryName = resources.getResourceEntryName(resourceId);
33970bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            final String resourceTypeName = resources.getResourceTypeName(resourceId);
33980bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            if (!expectedType.equals(resourceTypeName)) {
33990bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                Log.w(TAG, "Resource " + resourceId + " (" + resourceEntryName + ") is of type " +
34000bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                        resourceTypeName + " but " + expectedType + " is required.");
34010bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                return null;
34020bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            }
34030bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann
34040bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            return resourceEntryName;
34050bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann        } catch (NotFoundException e) {
34060bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            return null;
34070bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann        }
34080bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann    }
34090bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann
34104f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3411de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
3412bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3413b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "deleteInTransaction: " + uri);
3414b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
34155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
34165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
34175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
34185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.set(mDb);
34195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
34205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
3421b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3422f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3423f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
3424508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
3425508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
342635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
34275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE:
34285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().delete(mActiveDb.get(), selection,
34295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionArgs);
34305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
34315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case SYNCSTATE_ID: {
34325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                String selectionWithId =
34335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
34345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        + (selection == null ? "" : " AND (" + selection + ")");
34355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().delete(mActiveDb.get(), selectionWithId,
34365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionArgs);
34375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
343835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
34395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE_ID: {
3440b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3441b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3442b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
34435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mProfileHelper.getSyncState().delete(mActiveDb.get(), selectionWithId,
34445d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionArgs);
34455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
3446b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3447cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            case CONTACTS: {
3448cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // TODO
3449cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return 0;
3450cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3451cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3452d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
3453d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3454dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
34556bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
34566bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
34579fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP: {
34582e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
34592e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
34602e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
34615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
3462fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
34632e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
34642e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
34655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                final long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
3466dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
34672e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
34682e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
34699fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP_ID: {
34709fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                // lookup contact by id and lookup key to see if they still match the actual record
34719fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final List<String> pathSegments = uri.getPathSegments();
34729fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final String lookupKey = pathSegments.get(2);
34739fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
34749fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                setTablesAndProjectionMapForContacts(lookupQb, uri, null);
3475a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
34769fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                String[] args;
34779fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                if (selectionArgs == null) {
34789fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[2];
34799fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } else {
34809fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[selectionArgs.length + 2];
34819fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
34829fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
34839fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                args[0] = String.valueOf(contactId);
348460de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann                args[1] = Uri.encode(lookupKey);
34859fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                lookupQb.appendWhere(Contacts._ID + "=? AND " + Contacts.LOOKUP_KEY + "=?");
34865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = query(mActiveDb.get(), lookupQb, null, selection, args, null, null,
34875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        null);
34889fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                try {
34899fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    if (c.getCount() == 1) {
34909fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // contact was unmodified so go ahead and delete it
3491dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        return deleteContact(contactId, callerIsSyncAdapter);
34929fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    } else {
34939fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // row was changed (e.g. the merging might have changed), we got multiple
34949fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // rows or the supplied selection filtered the record out
34959fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        return 0;
34969fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    }
34979fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } finally {
34989fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    c.close();
34999fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
35009fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            }
35019fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
3502d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS:
3503d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS: {
35042971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
35055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS,
3506fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        new String[]{RawContacts._ID, RawContacts.CONTACT_ID},
3507e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
35082971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
35092971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
35102971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                        final long rawContactId = c.getLong(0);
3511fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        long contactId = c.getLong(1);
3512fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        numDeletes += deleteRawContact(rawContactId, contactId,
3513fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                                callerIsSyncAdapter);
35142971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
35152971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
35162971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
35172971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
35182971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
35192971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
35202971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
3521d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS_ID:
3522d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID: {
35232971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                final long rawContactId = ContentUris.parseId(uri);
35245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return deleteRawContact(rawContactId, mDbHelper.get().getContactId(rawContactId),
3525fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        callerIsSyncAdapter);
3526508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3527508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
35280c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case DATA:
35290c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case PROFILE_DATA: {
3530f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
3531944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                return deleteData(appendAccountToSelection(uri, selection), selectionArgs,
3532f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        callerIsSyncAdapter);
353320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
353420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
353548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
353648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
353748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
3538d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case POSTALS_ID:
3539d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_DATA_ID: {
3540508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
3541f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
35424da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
35434da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                return deleteData(Data._ID + "=?", mSelectionArgs1, callerIsSyncAdapter);
3544ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3545ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3546ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3547f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
35485aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteGroup(uri, ContentUris.parseId(uri), callerIsSyncAdapter);
35492971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
35502971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
35512971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case GROUPS: {
35522971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
35535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = mActiveDb.get().query(Tables.GROUPS, new String[]{Groups._ID},
3554e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
35552971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
35562971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
35575aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        numDeletes += deleteGroup(uri, c.getLong(0), callerIsSyncAdapter);
35582971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
35592971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
35602971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
35612971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
356281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (numDeletes > 0) {
3563f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
356481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
35652971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
3566508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3567508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
3568eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
356943880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3570e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                return deleteSettings(uri, appendAccountToSelection(uri, selection), selectionArgs);
3571eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3572eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
35735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case STATUS_UPDATES:
35745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_STATUS_UPDATES: {
35750a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                return deleteStatusUpdates(selection, selectionArgs);
35761f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
35771f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
35783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
35793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
35803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItems(uri, new ContentValues(), selection, selectionArgs);
35813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
35823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
35833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
35843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
35853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItems(uri, new ContentValues(),
35869b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann                        StreamItems._ID + "=?",
35873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{uri.getLastPathSegment()});
35883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
35893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
359082780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            case RAW_CONTACTS_ID_STREAM_ITEMS_ID: {
359182780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                mSyncToNetwork |= !callerIsSyncAdapter;
359282780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                String rawContactId = uri.getPathSegments().get(1);
359382780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                String streamItemId = uri.getLastPathSegment();
359482780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                return deleteStreamItems(uri, new ContentValues(),
359582780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        StreamItems.RAW_CONTACT_ID + "=? AND " + StreamItems._ID + "=?",
359682780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        new String[]{rawContactId, streamItemId});
359782780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
359882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            }
359982780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
36003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
36013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
36025d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                String streamItemId = uri.getPathSegments().get(1);
36035d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                String selectionWithId =
36045d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                        (StreamItemPhotos.STREAM_ITEM_ID + "=" + streamItemId + " ")
36055d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                                + (selection == null ? "" : " AND (" + selection + ")");
36065d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                return deleteStreamItemPhotos(uri, new ContentValues(),
36075d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                        selectionWithId, selectionArgs);
36083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
36093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
36103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
36113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
36123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
36133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
36143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItemPhotos(uri, new ContentValues(),
36153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=? AND "
36163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                                + StreamItemPhotos.STREAM_ITEM_ID + "=?",
36173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{streamItemPhotoId, streamItemId});
36183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
36193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
362081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
362181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
36223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
362381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
3624508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
36254f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
36264f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
36271c8e40c18f92722b9bec6e8ce2e345a9828efa16Dmitri Plotnikov    public int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) {
3628ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
36295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        final long groupMembershipMimetypeId = mDbHelper.get()
363094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
36315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.get().delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
363294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
363394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupId, null);
363494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
363594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        try {
3636f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (callerIsSyncAdapter) {
36375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mActiveDb.get().delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
363894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            } else {
363994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.clear();
364094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.put(Groups.DELETED, 1);
3641f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mValues.put(Groups.DIRTY, 1);
36425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mActiveDb.get().update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId,
36435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        null);
364494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            }
364594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        } finally {
36461a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
364794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
364894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
364994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
36505aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
36515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        final int count = mActiveDb.get().delete(Tables.SETTINGS, selection, selectionArgs);
36521a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        mVisibleTouched = true;
3653e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3654e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3655e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3656dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int deleteContact(long contactId, boolean callerIsSyncAdapter) {
365796b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(contactId);
36585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
365996b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                RawContacts.CONTACT_ID + "=?", mSelectionArgs1,
366096b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                null, null, null);
3661cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        try {
3662cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            while (c.moveToNext()) {
3663cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                long rawContactId = c.getLong(0);
3664dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
3665cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3666cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } finally {
3667cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            c.close();
3668cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
3669cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
36703826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
36713826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
36725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
3673cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3674cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3675fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov    public int deleteRawContact(long rawContactId, long contactId, boolean callerIsSyncAdapter) {
36765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().invalidateAggregationExceptionCache();
36773826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
36783826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
367982780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        // Find and delete stream items associated with the raw contact.
368082780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        Cursor c = mActiveDb.get().query(Tables.STREAM_ITEMS,
368182780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                new String[]{StreamItems._ID},
368282780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                StreamItems.RAW_CONTACT_ID + "=?", new String[]{String.valueOf(rawContactId)},
368382780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                null, null, null);
368482780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        try {
368582780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            while (c.moveToNext()) {
368682780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                deleteStreamItem(c.getLong(0));
368782780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            }
368882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        } finally {
368982780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            c.close();
369082780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        }
369182780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
3692d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro        if (callerIsSyncAdapter || rawContactIsLocal(rawContactId)) {
36935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().delete(Tables.PRESENCE,
36945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
36955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            int count = mActiveDb.get().delete(Tables.RAW_CONTACTS,
36965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    RawContacts._ID + "=" + rawContactId, null);
36975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mAggregator.get().updateDisplayNameForContact(mActiveDb.get(), contactId);
3698fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return count;
369933b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } else {
37005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mDbHelper.get().removeContactIfSingleton(rawContactId);
3701dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
370233b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        }
370333b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
370433b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
3705d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro    /**
3706d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro     * Returns whether the given raw contact ID is local (i.e. has no account associated with it).
3707d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro     */
3708d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro    private boolean rawContactIsLocal(long rawContactId) {
3709d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro        Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS,
3710d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                new String[] {
3711d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                        RawContacts.ACCOUNT_NAME,
3712d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                        RawContacts.ACCOUNT_TYPE,
3713d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                        RawContacts.DATA_SET
3714d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                },
3715d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                RawContacts._ID + "=?",
3716d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                new String[] {String.valueOf(rawContactId)}, null, null, null);
3717d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro        try {
3718d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            return c.moveToFirst() && c.isNull(0) && c.isNull(1) && c.isNull(2);
3719d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro        } finally {
3720d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            c.close();
3721d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro        }
3722d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro    }
3723d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro
37240a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private int deleteStatusUpdates(String selection, String[] selectionArgs) {
37259705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // delete from both tables: presence and status_updates
37269705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // TODO should account type/name be appended to the where clause?
37279705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      if (VERBOSE_LOGGING) {
37289705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          Log.v(TAG, "deleting data from status_updates for " + selection);
37299705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      }
37305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro      mActiveDb.get().delete(Tables.STATUS_UPDATES, getWhereClauseForStatusUpdatesTable(selection),
37319705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          selectionArgs);
37325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro      return mActiveDb.get().delete(Tables.PRESENCE, selection, selectionArgs);
37330a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
37340a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
37353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItems(Uri uri, ContentValues values, String selection,
37363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
37373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // First query for the stream items to be deleted, and check that they belong
37383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // to the account.
37393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
37403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemIds = enforceModifyingAccountForStreamItems(
37413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                account, selection, selectionArgs);
37423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
37433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If no security exception has been thrown, we're fine to delete.
37443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        for (long streamItemId : streamItemIds) {
37453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            deleteStreamItem(streamItemId);
37463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
37473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
37483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mVisibleTouched = true;
37493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemIds.size();
37503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
37513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
37523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItem(long streamItemId) {
37533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Note that this does not enforce the modifying account.
37543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        deleteStreamItemPhotos(streamItemId);
37555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().delete(Tables.STREAM_ITEMS, StreamItems._ID + "=?",
37563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{String.valueOf(streamItemId)});
37573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
37583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
37593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItemPhotos(Uri uri, ContentValues values, String selection,
37603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
37613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // First query for the stream item photos to be deleted, and check that they
37623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // belong to the account.
37633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
37643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItemPhotos(account, selection, selectionArgs);
37653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
37663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If no security exception has been thrown, we're fine to delete.
37675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().delete(Tables.STREAM_ITEM_PHOTOS, selection, selectionArgs);
37683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
37693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
37703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItemPhotos(long streamItemId) {
37713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Note that this does not enforce the modifying account.
37725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().delete(Tables.STREAM_ITEM_PHOTOS,
37735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                StreamItemPhotos.STREAM_ITEM_ID + "=?",
37743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{String.valueOf(streamItemId)});
37753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
37763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
3777dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int markRawContactAsDeleted(long rawContactId, boolean callerIsSyncAdapter) {
377881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = true;
377981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
3780cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.clear();
3781cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DELETED, 1);
3782cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
3783cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
3784cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
3785cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DIRTY, 1);
3786dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        return updateRawContact(rawContactId, mValues, callerIsSyncAdapter);
3787cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3788cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
37894f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3790de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
3791de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            String[] selectionArgs) {
3792bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3793b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "updateInTransaction: " + uri);
3794b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3795b5a4add17815167d20a90645779df34cdf45280dFred Quintana
37965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
37975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
37985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.set(mDb);
37995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
38005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
380135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
380200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
380300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
3804b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (match == SYNCSTATE_ID && selection == null) {
3805b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long rowId = ContentUris.parseId(uri);
38061129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Object data = values.get(ContactsContract.SyncState.DATA);
38075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mTransactionContext.get().syncStateUpdated(rowId, data);
3808b5a4add17815167d20a90645779df34cdf45280dFred Quintana            return 1;
3809b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3810b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3811f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3812f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
381300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
381435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
38155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE:
38165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().update(mActiveDb.get(), values,
3817b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        appendAccountToSelection(uri, selection), selectionArgs);
3818b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3819b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID: {
3820b5a4add17815167d20a90645779df34cdf45280dFred Quintana                selection = appendAccountToSelection(uri, selection);
3821b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3822b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3823b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
38245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().update(mActiveDb.get(), values,
38255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionWithId, selectionArgs);
38265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
38275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
38285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE_ID: {
38295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                selection = appendAccountToSelection(uri, selection);
38305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                String selectionWithId =
38315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
38325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        + (selection == null ? "" : " AND (" + selection + ")");
38335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mProfileHelper.getSyncState().update(mActiveDb.get(), values,
3834b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        selectionWithId, selectionArgs);
3835b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
383635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3837d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case CONTACTS:
3838d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE: {
3839dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(values, selection, selectionArgs, callerIsSyncAdapter);
384000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
384100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
384200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3843d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
3844dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(ContentUris.parseId(uri), values, callerIsSyncAdapter);
3845c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
3846c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
3847c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
38482e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
38492e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
38502e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
38512e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
38522e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
38535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
3854fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
38552e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
38562e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
38575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                final long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
3858dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(contactId, values, callerIsSyncAdapter);
38592e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                break;
38602e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
38612e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
3862d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS_DATA:
3863d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID_DATA: {
3864d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                int segment = match == RAW_CONTACTS_DATA ? 1 : 2;
3865d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                final String rawContactId = uri.getPathSegments().get(segment);
38667d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
38677d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                    + (selection == null ? "" : " AND " + selection);
38687d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
38697d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter);
38707d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
38717d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                break;
38727d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            }
38737d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
38740c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case DATA:
38750c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case PROFILE_DATA: {
3876944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                count = updateData(uri, values, appendAccountToSelection(uri, selection),
3877f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
387881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3879f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
388081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
388120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
388220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3883c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
388448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
388548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
388648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
388748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
3888f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
388981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3890f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
389181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
389200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
389300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
38947e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
38955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case RAW_CONTACTS:
38965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_RAW_CONTACTS: {
38975ac5c70d1165309302ebcc931f51723e37d31e0bJeff Sharkey                selection = appendAccountToSelection(uri, selection);
3898dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateRawContacts(values, selection, selectionArgs, callerIsSyncAdapter);
38997e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
39007e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
39017e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
39025ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
390333b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
39044529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                if (selection != null) {
39054da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
39064da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=?"
3907dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                                    + " AND(" + selection + ")", selectionArgs,
3908dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
39094529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                } else {
39104da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(rawContactId);
3911dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    count = updateRawContacts(values, RawContacts._ID + "=?", mSelectionArgs1,
3912dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
39134529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                }
39147e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
39157e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
39167e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
3917ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
39185aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, appendAccountToSelection(uri, selection),
3919f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
392081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3921f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
392281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3923ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3924ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3925ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3926ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3927ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
39284da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(groupId));
39294da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                String selectionWithId = Groups._ID + "=? "
393073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                        + (selection == null ? "" : " AND " + selection);
39315aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, selectionWithId, selectionArgs,
39325aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        callerIsSyncAdapter);
393381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3934f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
393581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3936ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3937ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3938ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3939127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
39405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                count = updateAggregationException(mActiveDb.get(), values);
3941b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
3942b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
3943b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
3944eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
3945e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                count = updateSettings(uri, values, appendAccountToSelection(uri, selection),
3946e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        selectionArgs);
394743880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3948eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
3949eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3950eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
39515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case STATUS_UPDATES:
39525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_STATUS_UPDATES: {
39539705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                count = updateStatusUpdate(uri, values, selection, selectionArgs);
39549705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                break;
39559705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            }
39569705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
39573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
39583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItems(uri, values, selection, selectionArgs);
39593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
39603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
39613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
39623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
39639b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann                count = updateStreamItems(uri, values, StreamItems._ID + "=?",
39643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{uri.getLastPathSegment()});
39653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
39663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
39673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
396882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            case RAW_CONTACTS_ID_STREAM_ITEMS_ID: {
396982780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                String rawContactId = uri.getPathSegments().get(1);
397082780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                String streamItemId = uri.getLastPathSegment();
397182780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                count = updateStreamItems(uri, values,
397282780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        StreamItems.RAW_CONTACT_ID + "=? AND " + StreamItems._ID + "=?",
397382780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        new String[]{rawContactId, streamItemId});
397482780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                break;
397582780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            }
397682780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
39773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
39783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values, selection, selectionArgs);
39793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
39803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
39813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
39823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
39833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
39843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values,
39853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotos.STREAM_ITEM_ID + "=?", new String[]{streamItemId});
39863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
39873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
39883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
39893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
39903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
39913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
39923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values,
39933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=? AND " +
39943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                                StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=?",
39953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{streamItemPhotoId, streamItemId});
39963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
39973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
39983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
399972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            case DIRECTORIES: {
4000bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                mContactDirectoryManager.scanPackagesByUid(Binder.getCallingUid());
400172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                count = 1;
4002d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
4003d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4004d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
400546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            case DATA_USAGE_FEEDBACK_ID: {
400646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (handleDataUsageFeedback(uri)) {
400746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    count = 1;
400846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                } else {
400946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    count = 0;
401046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
401146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                break;
401246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
401346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
401481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
401581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
4016f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
401781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
401800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
401900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
402000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
40214f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
40224f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
40239705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private int updateStatusUpdate(Uri uri, ContentValues values, String selection,
40249705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        String[] selectionArgs) {
40259705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // update status_updates table, if status is provided
40269705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO should account type/name be appended to the where clause?
40279705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        int updateCount = 0;
40289705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContentValues settableValues = getSettableColumnsForStatusUpdatesTable(values);
40299705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
40305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro          updateCount = mActiveDb.get().update(Tables.STATUS_UPDATES,
40319705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    settableValues,
40329705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    getWhereClauseForStatusUpdatesTable(selection),
40339705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selectionArgs);
40349705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
40359705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
40369705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // now update the Presence table
40379705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        settableValues = getSettableColumnsForPresenceTable(values);
40389705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
40395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro          updateCount = mActiveDb.get().update(Tables.PRESENCE, settableValues,
40409705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selection, selectionArgs);
40419705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
40429705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO updateCount is not entirely a valid count of updated rows because 2 tables could
40439705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // potentially get updated in this method.
40449705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return updateCount;
40459705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
40469705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
40473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int updateStreamItems(Uri uri, ContentValues values, String selection,
40483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
40493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Stream items can't be moved to a new raw contact.
40503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        values.remove(StreamItems.RAW_CONTACT_ID);
40513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
40523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check that the stream items being updated belong to the account.
40533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
40543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItems(account, selection, selectionArgs);
40553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
40566802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to update accounts params - they don't exist in the stream items table.
40576802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_NAME);
40586802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_TYPE);
40596802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
40603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If there's been no exception, the update should be fine.
40615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().update(Tables.STREAM_ITEMS, values, selection, selectionArgs);
40623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
40633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
40643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int updateStreamItemPhotos(Uri uri, ContentValues values, String selection,
40653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
40663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Stream item photos can't be moved to a new stream item.
40673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        values.remove(StreamItemPhotos.STREAM_ITEM_ID);
40683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
40693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check that the stream item photos being updated belong to the account.
40703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
40713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItemPhotos(account, selection, selectionArgs);
40723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
40736802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to update accounts params - they don't exist in the stream item
40746802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // photos table.
40756802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_NAME);
40766802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_TYPE);
40776802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
40786802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Process the photo (since we're updating, it's valid for the photo to not be present).
40796802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (processStreamItemPhoto(values, true)) {
40806802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // If there's been no exception, the update should be fine.
40815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return mActiveDb.get().update(Tables.STREAM_ITEM_PHOTOS, values, selection,
40825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    selectionArgs);
40836802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
40846802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        return 0;
40853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
40863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
40879705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    /**
40889705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     * Build a where clause to select the rows to be updated in status_updates table.
40899705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     */
40909705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private String getWhereClauseForStatusUpdatesTable(String selection) {
40919705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.setLength(0);
40929705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE);
40939705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(selection);
40949705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(")");
40959705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mSb.toString();
40969705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
40979705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
40989705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForStatusUpdatesTable(ContentValues values) {
40999705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
41009705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS, values,
41019705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS);
41029705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_TIMESTAMP, values,
41039705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_TIMESTAMP);
41049705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_RES_PACKAGE, values,
41059705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_RES_PACKAGE);
41069705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_LABEL, values,
41079705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_LABEL);
41089705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_ICON, values,
41099705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_ICON);
41109705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
41119705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
41129705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
41139705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForPresenceTable(ContentValues values) {
41149705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
41159705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.PRESENCE, values,
41169705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.PRESENCE);
4117aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.CHAT_CAPABILITY, values,
4118aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                StatusUpdates.CHAT_CAPABILITY);
41199705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
41209705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
41219705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
41225aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int updateGroups(Uri uri, ContentValues values, String selectionWithId,
4123f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
412473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
4125ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
4126ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
412773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        ContentValues updatedValues;
4128f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter && !values.containsKey(Groups.DIRTY)) {
412973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = mValues;
413073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.clear();
413173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.putAll(values);
413273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.put(Groups.DIRTY, 1);
413373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } else {
413473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = values;
413573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
413673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
41375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int count = mActiveDb.get().update(Tables.GROUPS, updatedValues, selectionWithId,
41385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                selectionArgs);
41391a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) {
41401a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
414194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
414243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
414343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        // TODO: This will not work for groups that have a data set specified, since the content
414443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        // resolver will not be able to request a sync for the right source (unless it is updated
414543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        // to key off account with data set).
41466ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        if (updatedValues.containsKey(Groups.SHOULD_SYNC)
41471129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                && updatedValues.getAsInteger(Groups.SHOULD_SYNC) != 0) {
41485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            Cursor c = mActiveDb.get().query(Tables.GROUPS, new String[]{Groups.ACCOUNT_NAME,
4149e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    Groups.ACCOUNT_TYPE}, selectionWithId, selectionArgs, null,
41506ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    null, null);
41516ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountName;
41526ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountType;
41536ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            try {
41546ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                while (c.moveToNext()) {
41556ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountName = c.getString(0);
41566ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountType = c.getString(1);
415724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
41586ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        Account account = new Account(accountName, accountType);
4159ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        ContentResolver.requestSync(account, ContactsContract.AUTHORITY,
41606ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                                new Bundle());
41616ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        break;
41626ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    }
41636ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                }
41646ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            } finally {
41656ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                c.close();
41666ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            }
41676ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        }
416894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        return count;
416994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
417094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
4171b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private int updateSettings(Uri uri, ContentValues values, String selection,
4172b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            String[] selectionArgs) {
41735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        final int count = mActiveDb.get().update(Tables.SETTINGS, values, selection, selectionArgs);
41741a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
41751a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
4176e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
4177e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
4178e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
4179e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
4180dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContacts(ContentValues values, String selection, String[] selectionArgs,
4181dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
41824529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (values.containsKey(RawContacts.CONTACT_ID)) {
41834529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            throw new IllegalArgumentException(RawContacts.CONTACT_ID + " should not be included " +
41844529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    "in content values. Contact IDs are assigned automatically");
41854529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
418673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
418797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
418897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
418997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0");
419097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
419197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
41924529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = 0;
41935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor cursor = mActiveDb.get().query(Views.RAW_CONTACTS,
419451bf5ea9531b9da72caff607dbdf35fd6f61cbe2Jeff Sharkey                new String[] { RawContacts._ID }, selection,
41954529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                selectionArgs, null, null, null);
41964529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        try {
41974529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            while (cursor.moveToNext()) {
41984529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                long rawContactId = cursor.getLong(0);
4199dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContact(rawContactId, values, callerIsSyncAdapter);
42004529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count++;
42014529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            }
42024529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        } finally {
42034529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            cursor.close();
42044529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
42054529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
42064529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return count;
42074529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    }
42084529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
4209dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContact(long rawContactId, ContentValues values,
4210dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
421196b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        final String selection = RawContacts._ID + " = ?";
421296b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(rawContactId);
421319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final boolean requestUndoDelete = (values.containsKey(RawContacts.DELETED)
421419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                && values.getAsInteger(RawContacts.DELETED) == 0);
421519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int previousDeleted = 0;
4216ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType = null;
4217ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName = null;
421843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String dataSet = null;
421919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        if (requestUndoDelete) {
42205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            Cursor cursor = mActiveDb.get().query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS,
42215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    selection, mSelectionArgs1, null, null, null);
422219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            try {
422319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                if (cursor.moveToFirst()) {
422419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    previousDeleted = cursor.getInt(RawContactsQuery.DELETED);
4225ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountType = cursor.getString(RawContactsQuery.ACCOUNT_TYPE);
4226ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountName = cursor.getString(RawContactsQuery.ACCOUNT_NAME);
422743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    dataSet = cursor.getString(RawContactsQuery.DATA_SET);
422819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                }
422919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            } finally {
423019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                cursor.close();
423119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
423219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
423319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT);
423419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        }
4235f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
42365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int count = mActiveDb.get().update(Tables.RAW_CONTACTS, values, selection, mSelectionArgs1);
42375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count != 0) {
4238f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            if (values.containsKey(RawContacts.AGGREGATION_MODE)) {
4239f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                int aggregationMode = values.getAsInteger(RawContacts.AGGREGATION_MODE);
4240f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov
4241f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // As per ContactsContract documentation, changing aggregation mode
4242f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // to DEFAULT should not trigger aggregation
4243f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                if (aggregationMode != RawContacts.AGGREGATION_MODE_DEFAULT) {
42445d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mAggregator.get().markForAggregation(rawContactId, aggregationMode, false);
4245f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                }
4246f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            }
4247433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            if (values.containsKey(RawContacts.STARRED)) {
4248dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter) {
4249dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
4250dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            values.getAsLong(RawContacts.STARRED) != 0);
4251dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
42525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mAggregator.get().updateStarred(rawContactId);
4253dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
4254dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // if this raw contact is being associated with an account, then update the
4255dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // favorites group membership based on whether or not this contact is starred.
4256dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // If it is starred, add a group membership, if one doesn't already exist
4257dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // otherwise delete any matching group memberships.
4258dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
42595d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    boolean starred = 0 != DatabaseUtils.longForQuery(mActiveDb.get(),
4260dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            SELECTION_STARRED_FROM_RAW_CONTACTS,
4261dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            new String[]{Long.toString(rawContactId)});
4262dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId, starred);
4263dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
4264dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
4265dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
4266dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // if this raw contact is being associated with an account, then add a
4267dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // group membership to the group marked as AutoAdd, if any.
4268dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
4269dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                addAutoAddMembership(rawContactId);
4270433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            }
4271dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
4272285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (values.containsKey(RawContacts.SOURCE_ID)) {
42735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mAggregator.get().updateLookupKeyForRawContact(mActiveDb.get(), rawContactId);
4274285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
4275f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            if (values.containsKey(RawContacts.NAME_VERIFIED)) {
4276f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
4277f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // If setting NAME_VERIFIED for this raw contact, reset it for all
4278f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // other raw contacts in the same aggregate
4279f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                if (values.getAsInteger(RawContacts.NAME_VERIFIED) != 0) {
42805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mDbHelper.get().resetNameVerifiedForOtherRawContacts(rawContactId);
4281f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                }
42825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mAggregator.get().updateDisplayNameForRawContact(mActiveDb.get(), rawContactId);
4283f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            }
428419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            if (requestUndoDelete && previousDeleted == 1) {
42855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mTransactionContext.get().rawContactInserted(rawContactId,
428643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        new AccountWithDataSet(accountName, accountType, dataSet));
428719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
42885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
42895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return count;
429033b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
429133b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
4292321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(Uri uri, ContentValues values, String selection,
4293f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
429420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
429520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
429620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
42975ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
429820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
429920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
430020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
430120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
430220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
43035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.get().getPackageId(packageName));
430420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
430520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
430697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
430797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
430897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    Data.IS_READ_ONLY + "=0");
430997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
431097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
4311653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        int count = 0;
431220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
4313653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
4314653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // so we don't need to worry about updating data we don't have permission to read.
43155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = queryLocal(uri,
4316f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                DataRowHandler.DataUpdateQuery.COLUMNS,
43175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                selection, selectionArgs, null, -1 /* directory ID */);
4318653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        try {
4319653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            while(c.moveToNext()) {
4320f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count += updateData(mValues, c, callerIsSyncAdapter);
432120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
4322653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        } finally {
4323653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            c.close();
432420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
432520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
4326653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return count;
432720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
432820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
4329f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
4330653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        if (values.size() == 0) {
4331653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return 0;
4332321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
4333653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
4334f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        final String mimeType = c.getString(DataRowHandler.DataUpdateQuery.MIMETYPE);
4335a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
4336f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        boolean updated =
43375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                rowHandler.update(mActiveDb.get(), mTransactionContext.get(), values, c,
43385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        callerIsSyncAdapter);
4339f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
4340f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
4341a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
4342f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        return updated ? 1 : 0;
4343321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    }
4344321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
43458c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(ContentValues values, String selection,
4346dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
43478c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        int count = 0;
43485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor cursor = mActiveDb.get().query(Views.CONTACTS,
43495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new String[] { Contacts._ID }, selection, selectionArgs, null, null, null);
43508c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        try {
43518c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            while (cursor.moveToNext()) {
43528c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                long contactId = cursor.getLong(0);
435324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
4354dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateContactOptions(contactId, values, callerIsSyncAdapter);
43558c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count++;
43568c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            }
43578c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        } finally {
43588c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            cursor.close();
43598c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        }
43608c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
43618c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return count;
43628c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
43638c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
4364dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateContactOptions(long contactId, ContentValues values,
4365dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
4366d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
43678c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
4368b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
4369d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
4370b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
4371d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
4372b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
4373d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
4374b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
4375d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
4376b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
4377d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.STARRED);
4378d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
4379d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
43808c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.size() == 0) {
4381d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
4382d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
4383d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
43848c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.containsKey(RawContacts.STARRED)) {
4385c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            // Mark dirty when changing starred to trigger sync
43868c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            mValues.put(RawContacts.DIRTY, 1);
4387c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey        }
4388c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey
43894da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(contactId);
43905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.get().update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=?"
439197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                + " AND " + RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0", mSelectionArgs1);
43928c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
4393dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (mValues.containsKey(RawContacts.STARRED) && !callerIsSyncAdapter) {
43945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            Cursor cursor = mActiveDb.get().query(Views.RAW_CONTACTS,
4395dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[] { RawContacts._ID }, RawContacts.CONTACT_ID + "=?",
4396dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    mSelectionArgs1, null, null, null);
4397dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            try {
4398dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                while (cursor.moveToNext()) {
4399dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    long rawContactId = cursor.getLong(0);
4400dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
4401dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            mValues.getAsLong(RawContacts.STARRED) != 0);
4402dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
4403dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } finally {
4404dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                cursor.close();
4405dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
4406dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
4407dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
44088c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // Copy changeable values to prevent automatically managed fields from
44098c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // being explicitly updated by clients.
44108c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
4411b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
44128c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
4413b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
44148c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
4415b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
44168c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
4417b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
44188c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
4419b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
44208c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.STARRED);
44218c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
44225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int rslt = mActiveDb.get().update(Tables.CONTACTS, mValues, Contacts._ID + "=?",
44235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mSelectionArgs1);
44246e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori
44259b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        if (values.containsKey(Contacts.LAST_TIME_CONTACTED) &&
44269b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori                !values.containsKey(Contacts.TIMES_CONTACTED)) {
44275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
44285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
44299b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        }
44309b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        return rslt;
4431f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
4432d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
4433127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
4434127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
44350c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId1 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID1);
44360c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId2 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID2);
443780c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
4438ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId1;
4439ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId2;
44400c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (rcId1 < rcId2) {
44410c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId1;
44420c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId2;
44430c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
44440c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId1;
44450c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId2;
4446b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
4447127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
44480c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
44494da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[0] = String.valueOf(rawContactId1);
44504da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[1] = String.valueOf(rawContactId2);
44510c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.delete(Tables.AGGREGATION_EXCEPTIONS,
44524da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    AggregationExceptions.RAW_CONTACT_ID1 + "=? AND "
44534da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    + AggregationExceptions.RAW_CONTACT_ID2 + "=?", mSelectionArgs2);
44540c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
44556bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            ContentValues exceptionValues = new ContentValues(3);
44566bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
44570c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
44580c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
44590c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
44600c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    exceptionValues);
4461127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
4462127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
44635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().invalidateAggregationExceptionCache();
44645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().markForAggregation(rawContactId1,
446569cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
44665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().markForAggregation(rawContactId2,
446769cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
4468dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov
44695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().aggregateContact(mTransactionContext.get(), db, rawContactId1);
44705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().aggregateContact(mTransactionContext.get(), db, rawContactId2);
4471127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
4472127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
4473127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
4474127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
4475b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
4476b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
447770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    public void onAccountsUpdated(Account[] accounts) {
4478bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
44793826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
44803826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
4481bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected boolean updateAccountsInBackground(Account[] accounts) {
4482f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao        // TODO : Check the unit test.
4483e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov        boolean accountsChanged = false;
44845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
44855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.set(db);
44865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        db.beginTransaction();
448770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        try {
448843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            Set<AccountWithDataSet> existingAccountsWithDataSets =
448943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    findValidAccountsWithDataSets(Tables.ACCOUNTS);
4490743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov
449143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // Add a row to the ACCOUNTS table (with no data set) for each new account.
4492743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            for (Account account : accounts) {
449343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                AccountWithDataSet accountWithDataSet = new AccountWithDataSet(
449443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        account.name, account.type, null);
449543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                if (!existingAccountsWithDataSets.contains(accountWithDataSet)) {
4496e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    accountsChanged = true;
449743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
449843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    // Add an account entry with an empty data set to match the account.
44995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME
450043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            + ", " + RawContacts.ACCOUNT_TYPE + ", " + RawContacts.DATA_SET
450143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            + ") VALUES (?, ?, ?)",
450243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            new String[] {
450343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getAccountName(),
450443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getAccountType(),
450543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getDataSet()
450643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            });
4507743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                }
4508743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            }
450948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
451043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // Check each of the existing sub-accounts against the account list.  If the owning
451143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // account no longer exists, the sub-account and all its data should be deleted.
451243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            List<AccountWithDataSet> accountsWithDataSetsToDelete =
451343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    new ArrayList<AccountWithDataSet>();
451443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            List<Account> accountList = Arrays.asList(accounts);
451543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            for (AccountWithDataSet accountWithDataSet : existingAccountsWithDataSets) {
451643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                Account owningAccount = new Account(
451743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        accountWithDataSet.getAccountName(), accountWithDataSet.getAccountType());
451843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                if (!accountList.contains(owningAccount)) {
451943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    accountsWithDataSetsToDelete.add(accountWithDataSet);
452043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                }
452170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
452270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
452343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            if (!accountsWithDataSetsToDelete.isEmpty()) {
4524e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                accountsChanged = true;
452543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                for (AccountWithDataSet accountWithDataSet : accountsWithDataSetsToDelete) {
452643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    Log.d(TAG, "removing data for removed account " + accountWithDataSet);
452743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    String[] accountParams = new String[] {
452843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            accountWithDataSet.getAccountName(),
452943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            accountWithDataSet.getAccountType()
453043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    };
453143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    String[] accountWithDataSetParams = accountWithDataSet.getDataSet() == null
453243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            ? accountParams
453343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            : new String[] {
453443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getAccountName(),
453543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getAccountType(),
453643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getDataSet()
453743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            };
453843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    String groupsDataSetClause = " AND " + Groups.DATA_SET
453943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            + (accountWithDataSet.getDataSet() == null ? " IS NULL" : " = ?");
454043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    String rawContactsDataSetClause = " AND " + RawContacts.DATA_SET
454143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            + (accountWithDataSet.getDataSet() == null ? " IS NULL" : " = ?");
4542f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                    String settingsDataSetClause = " AND " + Settings.DATA_SET
4543f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                            + (accountWithDataSet.getDataSet() == null ? " IS NULL" : " = ?");
454443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
45455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4546e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.GROUPS +
4547e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Groups.ACCOUNT_NAME + " = ?" +
454843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    " AND " + Groups.ACCOUNT_TYPE + " = ?" +
454943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    groupsDataSetClause, accountWithDataSetParams);
45505d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4551e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.PRESENCE +
4552e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (" +
4553e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    "SELECT " + RawContacts._ID +
4554e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " FROM " + Tables.RAW_CONTACTS +
4555e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
455643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    " AND " + RawContacts.ACCOUNT_TYPE + " = ?" +
455743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    rawContactsDataSetClause + ")", accountWithDataSetParams);
45585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4559c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            "DELETE FROM " + Tables.STREAM_ITEM_PHOTOS +
4560c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            " WHERE " + StreamItemPhotos.STREAM_ITEM_ID + " IN (" +
4561c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    "SELECT " + StreamItems._ID +
4562c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    " FROM " + Tables.STREAM_ITEMS +
4563c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    " WHERE " + StreamItems.RAW_CONTACT_ID + " IN (" +
4564c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                            "SELECT " + RawContacts._ID +
4565c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                            " FROM " + Tables.RAW_CONTACTS +
4566c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                            " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
4567c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                            " AND " + RawContacts.ACCOUNT_TYPE + " = ?" +
4568c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                            rawContactsDataSetClause + "))",
4569c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            accountWithDataSetParams);
4570c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                    db.execSQL(
4571c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            "DELETE FROM " + Tables.STREAM_ITEMS +
4572c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            " WHERE " + StreamItems.RAW_CONTACT_ID + " IN (" +
4573c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    "SELECT " + RawContacts._ID +
4574c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    " FROM " + Tables.RAW_CONTACTS +
4575c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
4576c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    " AND " + RawContacts.ACCOUNT_TYPE + " = ?" +
4577c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    rawContactsDataSetClause + ")",
4578c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            accountWithDataSetParams);
4579c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                    db.execSQL(
4580e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.RAW_CONTACTS +
4581e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
458243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            " AND " + RawContacts.ACCOUNT_TYPE + " = ?" +
458343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            rawContactsDataSetClause, accountWithDataSetParams);
45845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4585e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.SETTINGS +
4586e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Settings.ACCOUNT_NAME + " = ?" +
4587f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                            " AND " + Settings.ACCOUNT_TYPE + " = ?" +
4588f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                            settingsDataSetClause, accountWithDataSetParams);
45895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4590e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.ACCOUNTS +
4591e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + "=?" +
459243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            " AND " + RawContacts.ACCOUNT_TYPE + "=?" +
459343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            rawContactsDataSetClause, accountWithDataSetParams);
45945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4595d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            "DELETE FROM " + Tables.DIRECTORIES +
4596d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            " WHERE " + Directory.ACCOUNT_NAME + "=?" +
459743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            " AND " + Directory.ACCOUNT_TYPE + "=?", accountParams);
45984458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    resetDirectoryCache();
4599e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                }
4600e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov
460133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // Find all aggregated contacts that used to contain the raw contacts
460233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // we have just deleted and see if they are still referencing the deleted
4603e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                // names or photos.  If so, fix up those contacts.
460433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                HashSet<Long> orphanContactIds = Sets.newHashSet();
46055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor cursor = db.rawQuery("SELECT " + Contacts._ID +
460633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " FROM " + Tables.CONTACTS +
460733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " WHERE (" + Contacts.NAME_RAW_CONTACT_ID + " NOT NULL AND " +
460869cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                Contacts.NAME_RAW_CONTACT_ID + " NOT IN " +
460969cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + RawContacts._ID +
461069cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.RAW_CONTACTS + "))" +
461133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " OR (" + Contacts.PHOTO_ID + " NOT NULL AND " +
461233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                                Contacts.PHOTO_ID + " NOT IN " +
461369cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + Data._ID +
461469cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.DATA + "))", null);
461533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                try {
461633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    while (cursor.moveToNext()) {
461733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        orphanContactIds.add(cursor.getLong(0));
461833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    }
461933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                } finally {
462033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    cursor.close();
462133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
462233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
462333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                for (Long contactId : orphanContactIds) {
46245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mAggregator.get().updateAggregateData(mTransactionContext.get(), contactId);
462533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
46265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mDbHelper.get().updateAllVisible();
4627bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov                updateSearchIndexInTransaction();
462833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            }
462933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
463043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // Now that we've done the account-based additions and subtractions from the Accounts
463143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // table, check for raw contacts that have been added with a data set and add Accounts
463243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // entries for those if necessary.
463343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            existingAccountsWithDataSets = findValidAccountsWithDataSets(Tables.ACCOUNTS);
463443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            Set<AccountWithDataSet> rawContactAccountsWithDataSets =
463543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    findValidAccountsWithDataSets(Tables.RAW_CONTACTS);
463643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            rawContactAccountsWithDataSets.removeAll(existingAccountsWithDataSets);
463743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
463843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // Any remaining raw contact sub-accounts need to be added to the Accounts table.
463943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            for (AccountWithDataSet accountWithDataSet : rawContactAccountsWithDataSets) {
464043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                accountsChanged = true;
464143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
464243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                // Add an account entry to match the raw contact.
46435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                db.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME
464443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + ", " + RawContacts.ACCOUNT_TYPE + ", " + RawContacts.DATA_SET
464543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + ") VALUES (?, ?, ?)",
464643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        new String[] {
464743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                accountWithDataSet.getAccountName(),
464843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                accountWithDataSet.getAccountType(),
464943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                accountWithDataSet.getDataSet()
465043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        });
465143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
465243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
4653e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            if (accountsChanged) {
465443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                // TODO: Should sync state take data set into consideration?
46555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mDbHelper.get().getSyncState().onAccountsChanged(db, accounts);
4656e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            }
46575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db.setTransactionSuccessful();
465870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } finally {
46595d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db.endTransaction();
466070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
466173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.clear();
46623826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
46633826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (accountsChanged) {
46643826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateContactsAccountCount(accounts);
46653826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
46663826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
46673826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
4668afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov        return accountsChanged;
466970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
4670619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
46713826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateContactsAccountCount(Account[] accounts) {
46723826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        int count = 0;
46733826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        for (Account account : accounts) {
46743826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            if (isContactsAccount(account)) {
46753826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                count++;
46763826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            }
46773826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
46783826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mContactsAccountCount = count;
46793826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
46803826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
46813826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    protected boolean isContactsAccount(Account account) {
46823826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        final IContentService cs = ContentResolver.getContentService();
46833826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        try {
46843826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return cs.getIsSyncable(account, ContactsContract.AUTHORITY) > 0;
46853826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } catch (RemoteException e) {
46863826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            Log.e(TAG, "Cannot obtain sync flag for account: " + account, e);
46873826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return false;
46883826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
46893826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
46903826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
469172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void onPackageChanged(String packageName) {
4692bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_DIRECTORIES, packageName);
4693d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4694d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4695619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
469643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * Finds all distinct account types and data sets present in the specified table.
4697627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     */
469843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    private Set<AccountWithDataSet> findValidAccountsWithDataSets(String table) {
469943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        Set<AccountWithDataSet> accountsWithDataSets = new HashSet<AccountWithDataSet>();
47005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = mActiveDb.get().rawQuery(
470143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                "SELECT DISTINCT " + RawContacts.ACCOUNT_NAME + "," + RawContacts.ACCOUNT_TYPE +
470243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                "," + RawContacts.DATA_SET +
470343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                " FROM " + table, null);
4704627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
4705627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            while (c.moveToNext()) {
470691abbc9f691297594262d1f2d79acb744a66712cDave Santoro                if (!c.isNull(0) && !c.isNull(1)) {
470743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    accountsWithDataSets.add(
470843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            new AccountWithDataSet(c.getString(0), c.getString(1), c.getString(2)));
4709627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
4710627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
4711627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } finally {
4712627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            c.close();
4713627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
471443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        return accountsWithDataSets;
4715627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
4716627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
47174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
47184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
47194f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
472015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
472115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mReadAccessLatch);
472215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
47235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Query the profile DB if appropriate.
47245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDb(uri)) {
47255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToProfileMode();
47265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return mProfileProvider.query(uri, projection, selection, selectionArgs, sortOrder);
47275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
47285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
47295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Otherwise proceed with a normal query against the contacts DB.
47305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        switchToContactMode();
47315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.set(mContactsHelper.getReadableDatabase());
4732d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String directory = getQueryParameter(uri, ContactsContract.DIRECTORY_PARAM_KEY);
4733385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directory == null) {
4734b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return addSnippetExtrasToCursor(uri,
47355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder, -1));
4736385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directory.equals("0")) {
4737b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return addSnippetExtrasToCursor(uri,
47383716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder,
47395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            Directory.DEFAULT));
4740d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        } else if (directory.equals("1")) {
4741b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return addSnippetExtrasToCursor(uri,
47423716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder,
47435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            Directory.LOCAL_INVISIBLE));
4744d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4745d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4746d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        DirectoryInfo directoryInfo = getDirectoryAuthority(directory);
4747d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo == null) {
4748a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            Log.e(TAG, "Invalid directory ID: " + uri);
4749a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            return null;
4750d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4751d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4752d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Builder builder = new Uri.Builder();
4753d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.scheme(ContentResolver.SCHEME_CONTENT);
4754d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.authority(directoryInfo.authority);
4755d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.encodedPath(uri.getEncodedPath());
4756d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountName != null) {
4757d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_NAME, directoryInfo.accountName);
4758d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4759d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountType != null) {
4760d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, directoryInfo.accountType);
4761d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
47622e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
47632e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limit = getLimit(uri);
47642e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        if (limit != null) {
47652e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov            builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, limit);
47662e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        }
47672e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
4768d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Uri directoryUri = builder.build();
476909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
477009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        if (projection == null) {
477109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            projection = getDefaultProjection(uri);
477209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
477309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
4774332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        Cursor cursor = getContext().getContentResolver().query(directoryUri, projection, selection,
4775d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                selectionArgs, sortOrder);
47766ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
47776ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (cursor == null) {
47786ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return null;
47796ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
47806ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
4781547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        CrossProcessCursor crossProcessCursor = getCrossProcessCursor(cursor);
4782547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        if (crossProcessCursor != null) {
4783b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return addSnippetExtrasToCursor(uri, cursor);
4784547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        } else {
4785b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return matrixCursorFromCursor(addSnippetExtrasToCursor(uri, cursor));
4786547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        }
47873716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    }
47883716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
4789b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private Cursor addSnippetExtrasToCursor(Uri uri, Cursor cursor) {
4790547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro
4791547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        // If the cursor doesn't contain a snippet column, don't bother wrapping it.
4792547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        if (cursor.getColumnIndex(SearchSnippetColumns.SNIPPET) < 0) {
4793b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return cursor;
4794547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        }
4795547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro
47963716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // Parse out snippet arguments for use when snippets are retrieved from the cursor.
47973716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String[] args = null;
47983716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String snippetArgs =
47993716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                getQueryParameter(uri, SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY);
48003716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        if (snippetArgs != null) {
48013716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            args = snippetArgs.split(",");
48023716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        }
48033716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
48043716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String query = uri.getLastPathSegment();
48053716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String startMatch = args != null && args.length > 0 ? args[0]
48063716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_START_MATCH;
48073716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String endMatch = args != null && args.length > 1 ? args[1]
48083716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_END_MATCH;
48093716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String ellipsis = args != null && args.length > 2 ? args[2]
48103716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_ELLIPSIS;
48113716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        int maxTokens = args != null && args.length > 3 ? Integer.parseInt(args[3])
48123716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_MAX_TOKENS;
48133716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
4814b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        // Snippet data is needed for the snippeting on the client side, so store it in the cursor
4815b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        if (cursor instanceof AbstractCursor && deferredSnippetingRequested(uri)){
4816b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            Bundle oldExtras = cursor.getExtras();
4817b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            Bundle extras = new Bundle();
4818b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            if (oldExtras != null) {
4819b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                extras.putAll(oldExtras);
4820b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            }
4821b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            extras.putString(ContactsContract.DEFERRED_SNIPPETING_QUERY, query);
4822b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
4823b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            ((AbstractCursor) cursor).setExtras(extras);
48245517770250b3afa4fd88b6869c3244680821d222Dave Santoro        }
4825b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        return cursor;
4826b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    }
4827b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
4828b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private Cursor addDeferredSnippetingExtra(Cursor cursor) {
4829b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        if (cursor instanceof AbstractCursor){
4830b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            Bundle oldExtras = cursor.getExtras();
4831b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            Bundle extras = new Bundle();
4832b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            if (oldExtras != null) {
4833b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                extras.putAll(oldExtras);
4834b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            }
4835b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            extras.putBoolean(ContactsContract.DEFERRED_SNIPPETING, true);
4836b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            ((AbstractCursor) cursor).setExtras(extras);
4837b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        }
4838b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        return cursor;
48396ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
48406ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
48416ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    private CrossProcessCursor getCrossProcessCursor(Cursor cursor) {
48426ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        Cursor c = cursor;
48436ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (c instanceof CrossProcessCursor) {
48446ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return (CrossProcessCursor) c;
48456ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else if (c instanceof CursorWindow) {
48466ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return getCrossProcessCursor(((CursorWrapper) c).getWrappedCursor());
48476ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else {
48486ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return null;
48496ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
48506ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
48516ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
48526ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    public MatrixCursor matrixCursorFromCursor(Cursor cursor) {
48536ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        MatrixCursor newCursor = new MatrixCursor(cursor.getColumnNames());
48546ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        int numColumns = cursor.getColumnCount();
48556ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        String data[] = new String[numColumns];
48566ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        cursor.moveToPosition(-1);
48576ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        while (cursor.moveToNext()) {
48586ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            for (int i = 0; i < numColumns; i++) {
48596ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov                data[i] = cursor.getString(i);
48606ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            }
48616ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            newCursor.addRow(data);
4862332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        }
48636ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        return newCursor;
4864d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4865d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4866d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final class DirectoryQuery {
4867d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
4868d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory._ID,
4869d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.DIRECTORY_AUTHORITY,
4870d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_NAME,
4871d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_TYPE
4872d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        };
4873d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4874d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int DIRECTORY_ID = 0;
4875d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int AUTHORITY = 1;
4876d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_NAME = 2;
4877d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_TYPE = 3;
4878d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4879d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4880d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
4881d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Reads and caches directory information for the database.
4882d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
4883d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private DirectoryInfo getDirectoryAuthority(String directoryId) {
48844458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized (mDirectoryCache) {
48854458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            if (!mDirectoryCacheValid) {
48864458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCache.clear();
48875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                SQLiteDatabase db = mDbHelper.get().getReadableDatabase();
488849d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                Cursor cursor = db.query(Tables.DIRECTORIES,
48894458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryQuery.COLUMNS,
48904458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        null, null, null, null, null);
48914458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                try {
48924458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    while (cursor.moveToNext()) {
48934458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryInfo info = new DirectoryInfo();
48944458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        String id = cursor.getString(DirectoryQuery.DIRECTORY_ID);
48954458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.authority = cursor.getString(DirectoryQuery.AUTHORITY);
48964458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
48974458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
48984458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        mDirectoryCache.put(id, info);
48994458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    }
49004458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                } finally {
49014458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    cursor.close();
4902d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                }
49034458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCacheValid = true;
4904d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4905d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
49064458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            return mDirectoryCache.get(directoryId);
49074458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
4908d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4909d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
491072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void resetDirectoryCache() {
49114458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized(mDirectoryCache) {
49124458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            mDirectoryCacheValid = false;
49134458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
491472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
491572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
49165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    protected Cursor queryLocal(Uri uri, String[] projection, String selection,
49175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            String[] selectionArgs, String sortOrder, long directoryId) {
4918bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
4919bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            Log.v(TAG, "query: " + uri);
4920bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        }
49210b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov
49225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
49235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
49245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.set(mDb);
49255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
492635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4927d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
49281f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
4929c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limit = getLimit(uri);
4930b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        boolean snippetDeferred = false;
4931c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
49322ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        // The expression used in bundleLetterCountExtras() to get count.
49332ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        String addressBookIndexerCountExpression = null;
49342ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki
4935a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
49364f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
493735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
49385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE:
49395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().query(mActiveDb.get(), projection, selection,
49405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionArgs, sortOrder);
494135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4942d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
4943763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
49444b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                appendLocalDirectorySelectionIfNeeded(qb, directoryId);
4945619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
4946619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
4947619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
4948d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
49494a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
4950763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
49514da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
49524da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
49536bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
49546bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
49556bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
49565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP:
49575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
49585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
49595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int segmentCount = pathSegments.size();
49605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount < 3) {
49615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
4962fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
49635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
4964a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
49655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String lookupKey = pathSegments.get(2);
49665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount == 4) {
49675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
49685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
4969763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
4970a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
49715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
4972a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4973a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
4974a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
49755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        return c;
49765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
49775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
49785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
4979763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
49804da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
49815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        String.valueOf(lookupContactIdByLookupKey(mActiveDb.get(), lookupKey)));
49824da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
49835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                break;
49845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
49855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
49862149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_DATA:
4987bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_ID_DATA:
4988bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_PHOTO:
4989bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_ID_PHOTO: {
49902149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
49912149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                int segmentCount = pathSegments.size();
49922149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount < 4) {
49935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
49942149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                            "Missing a lookup key", uri));
49952149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
49962149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
49972149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount == 5) {
49982149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
49992149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
50002149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    setTablesAndProjectionMapForData(lookupQb, uri, projection, false);
5001bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    if (match == CONTACTS_LOOKUP_PHOTO || match == CONTACTS_LOOKUP_ID_PHOTO) {
5002bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
5003bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    }
5004a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
50055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
5006a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
5007a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Data.CONTACT_ID, contactId, Data.LOOKUP_KEY, lookupKey);
5008a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
50092149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                        return c;
50102149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    }
50112149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
50122149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    // TODO see if the contact exists but has no data rows (rare)
50132149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
50142149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
50152149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
50165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
50172149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
501824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        String.valueOf(contactId));
5019bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                if (match == CONTACTS_LOOKUP_PHOTO || match == CONTACTS_LOOKUP_ID_PHOTO) {
5020bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
5021bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                }
50222149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                qb.appendWhere(" AND " + Data.CONTACT_ID + "=?");
50232149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                break;
50242149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            }
50252149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
50263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_ID_STREAM_ITEMS: {
50273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                long contactId = Long.parseLong(uri.getPathSegments().get(1));
50283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
50293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
5030af10329f85c5d8c4196c495a9f0f9a6c6ecbc231Daniel Lehmann                qb.appendWhere(StreamItems.CONTACT_ID + "=?");
50313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
50323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
50333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
50343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_LOOKUP_STREAM_ITEMS:
50353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_LOOKUP_ID_STREAM_ITEMS: {
50363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                List<String> pathSegments = uri.getPathSegments();
50373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                int segmentCount = pathSegments.size();
50383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                if (segmentCount < 4) {
50395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
50403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                            "Missing a lookup key", uri));
50413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
50423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String lookupKey = pathSegments.get(2);
50433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                if (segmentCount == 5) {
50443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    long contactId = Long.parseLong(pathSegments.get(3));
50453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
50463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    setTablesAndProjectionMapForStreamItems(lookupQb);
50475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
50483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
5049af10329f85c5d8c4196c495a9f0f9a6c6ecbc231Daniel Lehmann                            StreamItems.CONTACT_ID, contactId,
5050af10329f85c5d8c4196c495a9f0f9a6c6ecbc231Daniel Lehmann                            StreamItems.CONTACT_LOOKUP_KEY, lookupKey);
50513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    if (c != null) {
50523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        return c;
50533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    }
50543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
50553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
50563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
50575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
50583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
50593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(RawContacts.CONTACT_ID + "=?");
50603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
50613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
50623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
5063f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
506442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
50655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
5066ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
5067f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                qb.setProjectionMap(sContactsVCardProjectionMap);
50684da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
506924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        String.valueOf(contactId));
50704da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
5071f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                break;
5072f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            }
5073f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
507442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
507542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
507642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                String currentDateString = dateFormat.format(new Date()).toString();
50775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mActiveDb.get().rawQuery(
507842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    "SELECT" +
507942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " 'vcards_' || ? || '.vcf' AS " + OpenableColumns.DISPLAY_NAME + "," +
508042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " NULL AS " + OpenableColumns.SIZE,
508142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    new String[] { currentDateString });
508242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
508342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
5084ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_FILTER: {
5085916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                String filterParam = "";
5086b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                boolean deferredSnipRequested = deferredSnippetingRequested(uri);
5087ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
5088916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    filterParam = uri.getLastPathSegment();
5089ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
50907ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov                setTablesAndProjectionMapForContactsWithSnippet(
5091b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                        qb, uri, projection, filterParam, directoryId,
5092b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                        deferredSnipRequested);
5093b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                snippetDeferred = isSingleWordQuery(filterParam) &&
5094b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                        deferredSnipRequested && snippetNeeded(projection);
5095ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
5096ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
5097ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
5098ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT_FILTER:
5099ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT: {
51002f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Basically the resultant SQL should look like this:
51012f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // (SQL for listing starred items)
51022f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // UNION ALL
51032f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // (SQL for listing frequently contacted items)
51042f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // ORDER BY ...
51052f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
51062f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final boolean phoneOnly = readBooleanQueryParameter(
51072f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        uri, ContactsContract.STREQUENT_PHONE_ONLY, false);
51082f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (match == CONTACTS_STREQUENT_FILTER && uri.getPathSegments().size() > 3) {
51094a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
51104a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
5111e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
51125e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
51132f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    selection = DbQueryUtils.concatenateClauses(selection, sb.toString());
51144a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
51154a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
51162f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                String[] subProjection = null;
51175e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                if (projection != null) {
51182f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    subProjection = appendProjectionArg(projection, TIMES_USED_SORT_COLUMN);
51195e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                }
51205e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
51214a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                // Build the first query for starred
51224928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                setTablesAndProjectionMapForContacts(qb, uri, projection, false);
51234928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                qb.setProjectionMap(phoneOnly ?
51244928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                        sStrequentPhoneOnlyStarredProjectionMap
51254928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                        : sStrequentStarredProjectionMap);
51269dbfd650ccf93714f3266e80f9fbdbcb526ae7b3Daisuke Miyakawa                if (phoneOnly) {
51275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    qb.appendWhere(DbQueryUtils.concatenateClauses(
51285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            selection, Contacts.HAS_PHONE_NUMBER + "=1"));
51299dbfd650ccf93714f3266e80f9fbdbcb526ae7b3Daisuke Miyakawa                }
51302f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                qb.setStrict(true);
51312f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final String starredQuery = qb.buildQuery(subProjection,
513224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        Contacts.STARRED + "=1", Contacts._ID, null, null, null);
5133d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
51342f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Reset the builder.
5135d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
51362f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                qb.setStrict(true);
51374928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
51384928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                // Build the second query for frequent part.
51394928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                final String frequentQuery;
51404928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                if (phoneOnly) {
51414928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    final StringBuilder tableBuilder = new StringBuilder();
51424928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // In phone only mode, we need to look at view_data instead of
51434928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // contacts/raw_contacts to obtain actual phone numbers. One problem is that
51444928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // view_data is much larger than view_contacts, so our query might become much
51454928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // slower.
51464928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    //
51474928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // To avoid the possible slow down, we start from data usage table and join
51484928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // view_data to the table, assuming data usage table is quite smaller than
51494928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // data rows (almost always it should be), and we don't want any phone
51504928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // numbers not used by the user. This way sqlite is able to drop a number of
51514928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // rows in view_data in the early stage of data lookup.
51524928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    tableBuilder.append(Tables.DATA_USAGE_STAT
51534928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + " INNER JOIN " + Views.DATA + " " + Tables.DATA
51544928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "="
51554928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                                + DataColumns.CONCRETE_ID + " AND "
51564928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "="
51574928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                                + DataUsageStatColumns.USAGE_TYPE_INT_CALL + ")");
51584928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    appendContactPresenceJoin(tableBuilder, projection, RawContacts.CONTACT_ID);
51594928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    appendContactStatusUpdateJoin(tableBuilder, projection,
51604928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            ContactsColumns.LAST_STATUS_UPDATE_ID);
51614928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
51624928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setTables(tableBuilder.toString());
51634928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setProjectionMap(sStrequentPhoneOnlyFrequentProjectionMap);
51644928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.appendWhere(DbQueryUtils.concatenateClauses(
51654928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            selection,
51664928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            Contacts.STARRED + "=0 OR " + Contacts.STARRED + " IS NULL",
51674928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            MimetypesColumns.MIMETYPE + " IN ("
51684928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + "'" + Phone.CONTENT_ITEM_TYPE + "', "
51694928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + "'" + SipAddress.CONTENT_ITEM_TYPE + "')"));
51704928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    frequentQuery = qb.buildQuery(subProjection, null, null, null, null, null);
51714928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                } else {
51724928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    setTablesAndProjectionMapForContacts(qb, uri, projection, true);
51734928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setProjectionMap(sStrequentFrequentProjectionMap);
51744928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.appendWhere(DbQueryUtils.concatenateClauses(
51754928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            selection,
51765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            "(" + Contacts.STARRED + " =0 OR " + Contacts.STARRED + " IS NULL)"));
51774928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    frequentQuery = qb.buildQuery(subProjection,
51784928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            null, Contacts._ID, null, null, null);
51794928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                }
5180d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
5181d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
51822f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final String unionQuery =
51832f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
51842f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                                STREQUENT_ORDER_BY, STREQUENT_LIMIT);
51852f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
51862f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Here, we need to use selection / selectionArgs (supplied from users) "twice",
51872f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // as we want them both for starred items and for frequently contacted items.
51882f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                //
51892f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // e.g. if the user specify selection = "starred =?" and selectionArgs = "0",
51902f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // the resultant SQL should be like:
51912f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // SELECT ... WHERE starred =? AND ...
51922f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // UNION ALL
51932f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // SELECT ... WHERE starred =? AND ...
51942f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                String[] doubledSelectionArgs = null;
51952f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (selectionArgs != null) {
51962f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    final int length = selectionArgs.length;
51972f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    doubledSelectionArgs = new String[length * 2];
51987d7d0e95636344c01eb4e4d034791c199bee98e9Daisuke Miyakawa                    System.arraycopy(selectionArgs, 0, doubledSelectionArgs, 0, length);
51997d7d0e95636344c01eb4e4d034791c199bee98e9Daisuke Miyakawa                    System.arraycopy(selectionArgs, 0, doubledSelectionArgs, length, length);
52002f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                }
52012f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
52025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor cursor = mActiveDb.get().rawQuery(unionQuery, doubledSelectionArgs);
52032f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (cursor != null) {
52042f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    cursor.setNotificationUri(getContext().getContentResolver(),
5205d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
5206d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
52072f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                return cursor;
5208d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
5209d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
521045ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa            case CONTACTS_FREQUENT: {
521145ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                setTablesAndProjectionMapForContacts(qb, uri, projection, true);
521245ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                qb.setProjectionMap(sStrequentFrequentProjectionMap);
521345ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                groupBy = Contacts._ID;
521445ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                if (!TextUtils.isEmpty(sortOrder)) {
521545ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                    sortOrder = FREQUENT_ORDER_BY + ", " + sortOrder;
521645ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                } else {
521745ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                    sortOrder = FREQUENT_ORDER_BY;
521845ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                }
521945ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                break;
522045ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa            }
522145ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa
5222ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_GROUP: {
5223763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
5224b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
522571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
52267cf50494501938f175d288077145acf49da8f171Daniel Lehmann                    String groupMimeTypeId = String.valueOf(
52277cf50494501938f175d288077145acf49da8f171Daniel Lehmann                            mDbHelper.get().getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
52284a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
52297cf50494501938f175d288077145acf49da8f171Daniel Lehmann                    selectionArgs = insertSelectionArg(selectionArgs, groupMimeTypeId);
5230b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
5231b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
5232b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
5233b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
523424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
523524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForContacts(qb, uri, projection);
523624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
523724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
523824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
523924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_ENTITIES: {
524024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForEntities(qb, uri, projection);
524124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
524224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
524324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
524424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD: {
5245ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
524624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.setProjectionMap(sContactsVCardProjectionMap);
524724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
524824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
524924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
5250a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_DATA: {
52514a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
525282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
52534da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
52544da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
52556bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
52566bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
525700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
5258a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
52593653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
526082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
52614da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
52624da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
52633653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
52643653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                break;
52653653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
52663653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
5267a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_ENTITIES: {
5268a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
5269a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
5270a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
5271a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
5272a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
5273a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
5274a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5275a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ENTITIES:
5276a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_ENTITIES: {
5277a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
5278a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                int segmentCount = pathSegments.size();
5279a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount < 4) {
52805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
5281a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            "Missing a lookup key", uri));
5282a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
5283a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
5284a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount == 5) {
5285a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
5286a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
5287a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    setTablesAndProjectionMapForEntities(lookupQb, uri, projection);
5288a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
5289a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
52905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
5291a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
5292a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.CONTACT_ID, contactId,
5293a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.LOOKUP_KEY, lookupKey);
5294a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
5295a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        return c;
5296a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    }
5297a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
5298a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5299a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
5300a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
53015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        String.valueOf(lookupContactIdByLookupKey(mActiveDb.get(), lookupKey)));
5302a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + Contacts.Entity.CONTACT_ID + "=?");
5303a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
5304a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
5305a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
53063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
53073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
53083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
53093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
53103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
53113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
53123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
53133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
53149b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann                qb.appendWhere(StreamItems._ID + "=?");
53153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
53163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
53173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
53183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_LIMIT: {
53196802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                MatrixCursor cursor = new MatrixCursor(new String[]{StreamItems.MAX_ITEMS}, 1);
53206802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                cursor.addRow(new Object[]{MAX_STREAM_ITEMS_PER_RAW_CONTACT});
53213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return cursor;
53223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
53233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
53243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
53253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
53263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
53273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
53283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
53293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
53303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
53313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
53323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemId);
53333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=?");
53343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
53353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
53363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
53373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
53383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
53393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
53403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
53413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemPhotoId);
53423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemId);
53433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=? AND " +
53443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=?");
53453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
53463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
53473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
5348f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case PHOTO_DIMENSIONS: {
5349f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                MatrixCursor cursor = new MatrixCursor(
5350f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{DisplayPhoto.DISPLAY_MAX_DIM, DisplayPhoto.THUMBNAIL_MAX_DIM},
5351f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        1);
5352f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                cursor.addRow(new Object[]{mMaxDisplayPhotoDim, mMaxThumbnailPhotoDim});
5353f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                return cursor;
5354f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
5355f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
53564a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case PHONES: {
535782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
53587cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + "=" +
53597cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        mDbHelper.get().getMimeTypeIdForPhone());
53602ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki
53612ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                // Dedupe phone numbers per contact.
5362cf55cbe8932f620484a3634d13ecc116c32fdc99Daisuke Miyakawa                groupBy = RawContacts.CONTACT_ID + ", " + Data.DATA1;
53632ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki
53642ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                // In this case, because we dedupe phone numbers, the address book indexer needs
53652ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                // to take it into account too.  (Otherwise headers will appear in wrong positions.)
53662ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                // So use count(distinct pair(CONTACT_ID, PHONE NUMBER)) instead of count(*).
53672ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                // But because there's no such thing as pair() on sqlite, we use
53682ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                // CONTACT_ID || ',' || PHONE NUMBER instead.
53692ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                // This only slows down the query by 14% with 10,000 contacts.
53702ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                addressBookIndexerCountExpression = "DISTINCT "
53712ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                        + RawContacts.CONTACT_ID + "||','||" + Data.DATA1;
53722815f58f72f109790585931f601a63ddc02536a5Evan Millar                break;
53732815f58f72f109790585931f601a63ddc02536a5Evan Millar            }
53742815f58f72f109790585931f601a63ddc02536a5Evan Millar
537548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID: {
537682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
53774da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
53787cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
53797cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForPhone());
53804da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
538148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
538248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
538348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
5384ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
538546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
538646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                Integer typeInt = sDataUsageTypeMap.get(typeParam);
538746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (typeInt == null) {
538846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    typeInt = DataUsageStatColumns.USAGE_TYPE_INT_CALL;
538946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
539046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
53917cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
53927cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForPhone());
5393ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
53944a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
53954a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
5396a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    sb.append(" AND (");
53975e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
539845d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    boolean hasCondition = false;
53995e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    boolean orNeeded = false;
54005e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String normalizedName = NameNormalizer.normalize(filterParam);
54015e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (normalizedName.length() > 0) {
5402155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN " +
5403155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
5404155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.SEARCH_INDEX +
5405155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " JOIN " + Tables.RAW_CONTACTS +
5406155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
5407155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
5408155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE " + SearchIndexColumns.NAME + " MATCH ");
54092352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                        DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filterParam) + "*");
5410155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(")");
54115e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        orNeeded = true;
541245d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
54135e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
54145e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
5415892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    String number = PhoneNumberUtils.normalizeNumber(filterParam);
5416892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (!TextUtils.isEmpty(number)) {
54175e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        if (orNeeded) {
54185e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                            sb.append(" OR ");
54195e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        }
54205e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data._ID +
5421892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                " IN (SELECT DISTINCT " + PhoneLookupColumns.DATA_ID
5422892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " FROM " + Tables.PHONE_LOOKUP
5423892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
5424892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append(number);
5425892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append("%')");
542645d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
542745d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    }
542845d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov
542945d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    if (!hasCondition) {
543045d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // If it is neither a phone number nor a name, the query should return
543145d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // an empty cursor.  Let's ensure that.
543245d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        sb.append("0");
54335e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
54345e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
5435a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
5436ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
543758567abca253f1efa2db5c39e17e42dca589e916Daisuke Miyakawa                groupBy = "(CASE WHEN " + PhoneColumns.NORMALIZED_NUMBER
543858567abca253f1efa2db5c39e17e42dca589e916Daisuke Miyakawa                        + " IS NOT NULL THEN " + PhoneColumns.NORMALIZED_NUMBER
543958567abca253f1efa2db5c39e17e42dca589e916Daisuke Miyakawa                        + " ELSE " + Phone.NUMBER + " END), " + RawContacts.CONTACT_ID;
5440a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
544146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
544246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
544346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = accountPromotionSortOrder + ", " + PHONE_FILTER_SORT_ORDER;
544446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    } else {
544546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = PHONE_FILTER_SORT_ORDER;
544646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    }
5447a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
5448ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
5449ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
5450ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
54514a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case EMAILS: {
545282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
54537cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
54547cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForEmail());
54554a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                break;
54564a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
54574a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
545848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID: {
545982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
54604da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
54617cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
54627cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForEmail()
54634da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        + " AND " + Data._ID + "=?");
546448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
546548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
546648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
54675e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_LOOKUP: {
546882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
54697cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
54707cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForEmail());
54714a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
547208768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String email = uri.getLastPathSegment();
54735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    String address = mDbHelper.get().extractAddressFromEmailAddress(email);
547408768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, address);
547508768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    qb.appendWhere(" AND UPPER(" + Email.DATA + ")=UPPER(?)");
54764a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
5477ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
5478ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
5479ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
54805e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_FILTER: {
548146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
548246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                Integer typeInt = sDataUsageTypeMap.get(typeParam);
548346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (typeInt == null) {
548446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    typeInt = DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT;
548546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
548646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
548707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                String filterParam = null;
54887d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa
548907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
549007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    filterParam = uri.getLastPathSegment();
549107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    if (TextUtils.isEmpty(filterParam)) {
549207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                        filterParam = null;
549307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    }
549407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                }
54955e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
549607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (filterParam == null) {
549707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    // If the filter is unspecified, return nothing
549807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    qb.appendWhere(" AND 0");
549907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                } else {
550007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    StringBuilder sb = new StringBuilder();
550107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(" AND " + Data._ID + " IN (");
550207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(
550307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            "SELECT " + Data._ID +
550407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " FROM " + Tables.DATA +
55052a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                            " WHERE " + DataColumns.MIMETYPE_ID + "=");
55065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    sb.append(mDbHelper.get().getMimeTypeIdForEmail());
55072a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                    sb.append(" AND " + Data.DATA1 + " LIKE ");
550807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(sb, filterParam + '%');
550920938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                    if (!filterParam.contains("@")) {
5510155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(
5511155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " UNION SELECT " + Data._ID +
5512155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.DATA +
5513155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE +" + DataColumns.MIMETYPE_ID + "=");
55145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        sb.append(mDbHelper.get().getMimeTypeIdForEmail());
5515155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(" AND " + Data.RAW_CONTACT_ID + " IN " +
5516155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
5517155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.SEARCH_INDEX +
5518155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " JOIN " + Tables.RAW_CONTACTS +
5519155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
5520155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
5521155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE " + SearchIndexColumns.NAME + " MATCH ");
55222352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                        DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filterParam) + "*");
5523155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(")");
55245e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
55255e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
5526a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
55275e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                }
55285e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
5529a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
553046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
553146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
553246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = accountPromotionSortOrder + ", " + EMAIL_FILTER_SORT_ORDER;
55337d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                    } else {
55347d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                        sortOrder = EMAIL_FILTER_SORT_ORDER;
55357d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                    }
5536a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
55375e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                break;
55385e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
55395e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
5540ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
554182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
55427cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
55437cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForStructuredPostal());
5544ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
5545ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
5546ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
554748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
554882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
55494da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
55507cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
55517cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForStructuredPostal());
55524da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
555348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
555448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
555548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
5556d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS:
5557d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS: {
5558763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
55594f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
55604f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
55614f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
5562d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS_ID:
5563d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID: {
55645ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
5565763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
55664da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
55674da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
55684f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
55694f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
55704f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
5571d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS_DATA:
5572d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID_DATA: {
5573d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                int segment = match == RAW_CONTACTS_DATA ? 1 : 2;
5574d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(segment));
557582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
55764da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
55774da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=?");
557824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
557924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
558024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
55813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case RAW_CONTACTS_ID_STREAM_ITEMS: {
55823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
55833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
55843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
55853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItems.RAW_CONTACT_ID + "=?");
55863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
55873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
558824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
558982780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            case RAW_CONTACTS_ID_STREAM_ITEMS_ID: {
559082780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
559182780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                long streamItemId = Long.parseLong(uri.getPathSegments().get(3));
559282780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                setTablesAndProjectionMapForStreamItems(qb);
559382780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(streamItemId));
5594c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
559582780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                qb.appendWhere(StreamItems.RAW_CONTACT_ID + "=? AND " +
559682780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        StreamItems._ID + "=?");
559782780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                break;
559882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            }
559982780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
560024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID_ENTITIES: {
560124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(2));
560224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
560324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForRawEntities(qb, uri);
56045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                qb.appendWhere(" AND " + RawContacts._ID + "=?");
5605e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
5606e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
5607e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
5608d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case DATA:
5609d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_DATA: {
561082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
5611e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
5612e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
5613e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
5614d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case DATA_ID:
5615d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_DATA_ID: {
561682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
56174da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
56184da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
5619a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                break;
5620a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            }
5621a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov
5622a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
56234a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
5624a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
5625a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
5626a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
5627892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    sortOrder = " length(lookup.normalized_number) DESC";
5628a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
5629a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
5630e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
5631e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                String numberE164 = PhoneNumberUtils.formatNumberToE164(number,
56325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mDbHelper.get().getCurrentCountryIso());
5633892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                String normalizedNumber =
5634892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        PhoneNumberUtils.normalizeNumber(number);
56355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mDbHelper.get().buildPhoneLookupAndContactQuery(qb, normalizedNumber, numberE164);
5636e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                qb.setProjectionMap(sPhoneLookupProjectionMap);
5637e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                // Phone lookup cannot be combined with a selection
5638e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selection = null;
5639e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selectionArgs = null;
5640a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
5641a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
5642a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
5643ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
5644ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.GROUPS);
5645ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
5646f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                appendAccountFromParameter(qb, uri);
5647ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5648ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5649ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5650ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
5651ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.GROUPS);
5652ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
56534da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
56544da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Groups._ID + "=?");
5655ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5656ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5657ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5658ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
5659f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                final boolean returnGroupCountPerAccount =
5660f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        readBooleanQueryParameter(uri, Groups.PARAM_RETURN_GROUP_COUNT_PER_ACCOUNT,
5661f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                                false);
5662f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                qb.setTables(Views.GROUPS + " AS " + Tables.GROUPS);
5663f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                qb.setProjectionMap(returnGroupCountPerAccount ?
5664f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        sGroupsSummaryProjectionMapWithGroupCountPerAccount
5665f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        : sGroupsSummaryProjectionMap);
5666f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                appendAccountFromParameter(qb, uri);
5667f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                groupBy = GroupsColumns.CONCRETE_ID;
5668ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5669ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5670ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5671b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
56720c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
5673b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
5674b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
5675b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
5676b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
567731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
5678d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
56792d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                String filter = null;
56802d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
56812d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                    filter = uri.getPathSegments().get(3);
56822d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                }
568331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
5684d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                if (limit != null) {
5685d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(limit);
568631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
568731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
568831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
568931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
56905b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                ArrayList<AggregationSuggestionParameter> parameters = null;
56915b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                List<String> query = uri.getQueryParameters("query");
56925b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                if (query != null && !query.isEmpty()) {
56935b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    parameters = new ArrayList<AggregationSuggestionParameter>(query.size());
56945b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    for (String parameter : query) {
56955b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        int offset = parameter.indexOf(':');
56965b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        parameters.add(offset == -1
56975b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                ? new AggregationSuggestionParameter(
569876dfa406e2cde19c824983c37fc92c1c5bf63eecDaniel Lehmann                                        AggregationSuggestions.PARAMETER_MATCH_NAME,
56995b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter)
57005b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                : new AggregationSuggestionParameter(
57015b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(0, offset),
57025b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(offset + 1)));
57035b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    }
57045b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                }
57055b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
5706763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
57077581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov
57085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mAggregator.get().queryAggregationSuggestions(qb, projection, contactId,
57095b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        maxSuggestions, filter, parameters);
571031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
571131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
5712eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
5713eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setTables(Tables.SETTINGS);
5714eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setProjectionMap(sSettingsProjectionMap);
5715f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                appendAccountFromParameter(qb, uri);
5716e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
5717e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // When requesting specific columns, this query requires
5718e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // late-binding of the GroupMembership MIME-type.
57195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                final String groupMembershipMimetypeId = Long.toString(mDbHelper.get()
5720e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
572182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
57225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mDbHelper.get().isInProjection(projection, Settings.UNGROUPED_COUNT)) {
5723e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
5724e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
572582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
57265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mDbHelper.get().isInProjection(
57275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                                projection, Settings.UNGROUPED_WITH_PHONES)) {
5728e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
5729e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
5730e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
5731eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
5732eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
5733eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
57345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case STATUS_UPDATES:
57355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_STATUS_UPDATES: {
57360a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
57375ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
57385ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
57395ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
574082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES_ID: {
57410a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
57424da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
57434da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(DataColumns.CONCRETE_ID + "=?");
57445ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
57455ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
57465ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
5747c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS: {
5748174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(
57495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mActiveDb.get(), uri, projection, limit);
5750c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5751c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
5752c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT: {
57532d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                String lookupKey = uri.getLastPathSegment();
5754174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                String filter = getQueryParameter(
5755174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        uri, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
5756174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchShortcutRefresh(
57575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mActiveDb.get(), projection, lookupKey, filter);
5758c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5759c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
57601b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS:
5761ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
57621b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
57631b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
57641b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
57651b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_WITH_PHONES:
5766ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
57671b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
57681b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.HAS_PHONE_NUMBER + "=1");
57691b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
57701b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
57711b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_FAVORITES:
5772ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
57731b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
57741b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.STARRED + "=1");
57751b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
57761b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
57771b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_GROUP_NAME:
5778ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
57791b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
578071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
57817cf50494501938f175d288077145acf49da8f171Daniel Lehmann                String groupMimeTypeId = String.valueOf(
57827cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        mDbHelper.get().getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
57831b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
57847cf50494501938f175d288077145acf49da8f171Daniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, groupMimeTypeId);
57851b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
57861b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
57873202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro            case RAW_CONTACT_ENTITIES:
57883202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro            case PROFILE_RAW_CONTACT_ENTITIES: {
5789a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
579046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
579146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
579246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
579346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITY_ID: {
579446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
5795a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
57964da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
57974da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
579846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
579946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
580046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
580109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            case PROVIDER_STATUS: {
580209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                return queryProviderStatus(uri, projection);
580309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
580409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5805d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES : {
5806d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
5807d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
5808d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
5809d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
5810d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
5811d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID : {
5812385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                long id = ContentUris.parseId(uri);
5813d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
5814d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
5815385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(id));
5816d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.appendWhere(Directory._ID + "=?");
5817d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
5818d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
5819d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
58207a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            case COMPLETE_NAME: {
58217a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                return completeName(uri, projection);
58227a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
58237a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
58244f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
5825f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
5826c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                        sortOrder, limit);
58274f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
58284f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
582909e69522745551522c55dff27424496f255def46Daniel Lehmann        qb.setStrict(true);
58307f786e5cbde9975b9632beb9b6d19eeef8a64cf1Dmitri Plotnikov
5831ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        Cursor cursor =
58325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                query(mActiveDb.get(), qb, projection, selection, selectionArgs, sortOrder, groupBy,
58335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        limit);
5834ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (readBooleanQueryParameter(uri, ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, false)) {
58355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            cursor = bundleLetterCountExtras(cursor, mActiveDb.get(), qb, selection,
58362ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                    selectionArgs, sortOrder, addressBookIndexerCountExpression);
5837ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5838b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        if (snippetDeferred) {
5839b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            cursor = addDeferredSnippetingExtra(cursor);
5840b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        }
5841ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        return cursor;
58425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
58435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
58445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
58455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String selection, String[] selectionArgs, String sortOrder, String groupBy,
58465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String limit) {
5847038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        if (projection != null && projection.length == 1
5848038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                && BaseColumns._COUNT.equals(projection[0])) {
5849038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            qb.setProjectionMap(sCountProjectionMap);
5850038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
58515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
58525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sortOrder, limit);
58534f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
58544f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
58554f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
58564f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
58574f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
58584f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
585909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    /**
586009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     * Creates a single-row cursor containing the current status of the provider.
586109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     */
586209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private Cursor queryProviderStatus(Uri uri, String[] projection) {
586309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
586409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        RowBuilder row = cursor.newRow();
586509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
586609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            if (ProviderStatus.STATUS.equals(projection[i])) {
586709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mProviderStatus);
586809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            } else if (ProviderStatus.DATA1.equals(projection[i])) {
586909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mEstimatedStorageRequirement);
587009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
587109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        }
587209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        return cursor;
587309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    }
587409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5875a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /**
5876a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * Runs the query with the supplied contact ID and lookup ID.  If the query succeeds,
5877a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * it returns the resulting cursor, otherwise it returns null and the calling
5878a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * method needs to resolve the lookup key and rerun the query.
5879a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     */
5880a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private Cursor queryWithContactIdAndLookupKey(SQLiteQueryBuilder lookupQb,
5881a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            SQLiteDatabase db, Uri uri,
5882a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection, String selection, String[] selectionArgs,
5883a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String sortOrder, String groupBy, String limit,
5884a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn, long contactId, String lookupKeyColumn, String lookupKey) {
5885a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        String[] args;
5886a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (selectionArgs == null) {
5887a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[2];
5888a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        } else {
5889a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[selectionArgs.length + 2];
5890a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
5891a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5892a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[0] = String.valueOf(contactId);
5893a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[1] = Uri.encode(lookupKey);
5894a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        lookupQb.appendWhere(contactIdColumn + "=? AND " + lookupKeyColumn + "=?");
5895a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        Cursor c = query(db, lookupQb, projection, selection, args, sortOrder,
5896a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                groupBy, limit);
5897a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (c.getCount() != 0) {
5898a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return c;
5899a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5900a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5901a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        c.close();
5902a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return null;
5903a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
590409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5905bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov    private static final class AddressBookIndexQuery {
5906bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String LETTER = "letter";
5907bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String TITLE = "title";
5908bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String COUNT = "count";
5909ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5910bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
5911bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                LETTER, TITLE, COUNT
5912ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        };
5913ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5914bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_LETTER = 0;
5915bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_TITLE = 1;
5916bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_COUNT = 2;
5917bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
59185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // The first letter of the sort key column is what is used for the index headings.
59195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        public static final String SECTION_HEADING = "SUBSTR(%1$s,1,1)";
592024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
5921de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa        public static final String ORDER_BY = LETTER + " COLLATE " + PHONEBOOK_COLLATOR_NAME;
5922ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
5923ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5924ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    /**
5925ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * Computes counts by the address book index titles and adds the resulting tally
5926ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * to the returned cursor as a bundle of extras.
5927ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     */
5928ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    private Cursor bundleLetterCountExtras(Cursor cursor, final SQLiteDatabase db,
59292ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki            SQLiteQueryBuilder qb, String selection, String[] selectionArgs, String sortOrder,
59302ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki            String countExpression) {
5931409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki        if (!(cursor instanceof AbstractCursor)) {
5932409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            Log.w(TAG, "Unable to bundle extras.  Cursor is not AbstractCursor.");
5933409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            return cursor;
5934409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki        }
5935ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortKey;
5936ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5937ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // The sort order suffix could be something like "DESC".
5938ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // We want to preserve it in the query even though we will change
5939ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // the sort column itself.
5940ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortOrderSuffix = "";
5941ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (sortOrder != null) {
5942ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int spaceIndex = sortOrder.indexOf(' ');
5943ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            if (spaceIndex != -1) {
5944ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder.substring(0, spaceIndex);
5945ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortOrderSuffix = sortOrder.substring(spaceIndex);
5946ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            } else {
5947ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder;
5948ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
5949ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } else {
5950ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            sortKey = Contacts.SORT_KEY_PRIMARY;
5951ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5952ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5953bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        String locale = getLocale().toString();
5954ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        HashMap<String, String> projectionMap = Maps.newHashMap();
59555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        String sectionHeading = String.format(AddressBookIndexQuery.SECTION_HEADING, sortKey);
5956bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.LETTER,
595724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                sectionHeading + " AS " + AddressBookIndexQuery.LETTER);
5958bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
59592ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        // If "what to count" is not specified, we just count all records.
59602ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        if (TextUtils.isEmpty(countExpression)) {
59612ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki            countExpression = "*";
59622ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        }
59632ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki
5964bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        /**
5965bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * Use the GET_PHONEBOOK_INDEX function, which is an android extension for SQLite3,
5966bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * to map the first letter of the sort key to a character that is traditionally
5967bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * used in phonebooks to represent that letter.  For example, in Korean it will
5968bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * be the first consonant in the letter; for Japanese it will be Hiragana rather
5969bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * than Katakana.
5970bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         */
5971ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.TITLE,
597224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                "GET_PHONEBOOK_INDEX(" + sectionHeading + ",'" + locale + "')"
5973bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                        + " AS " + AddressBookIndexQuery.TITLE);
5974ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.COUNT,
59752ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                "COUNT(" + countExpression + ") AS " + AddressBookIndexQuery.COUNT);
5976ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        qb.setProjectionMap(projectionMap);
5977ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5978f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov        Cursor indexCursor = qb.query(db, AddressBookIndexQuery.COLUMNS, selection, selectionArgs,
5979ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY, null /* having */,
5980ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY + sortOrderSuffix);
5981ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5982ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        try {
5983f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            int groupCount = indexCursor.getCount();
5984ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            String titles[] = new String[groupCount];
5985ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int counts[] = new int[groupCount];
5986bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            int indexCount = 0;
5987bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            String currentTitle = null;
5988bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5989bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // Since GET_PHONEBOOK_INDEX is a many-to-1 function, we may end up
5990bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // with multiple entries for the same title.  The following code
5991bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // collapses those duplicates.
5992ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            for (int i = 0; i < groupCount; i++) {
5993f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov                indexCursor.moveToNext();
5994bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String title = indexCursor.getString(AddressBookIndexQuery.COLUMN_TITLE);
5995bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int count = indexCursor.getInt(AddressBookIndexQuery.COLUMN_COUNT);
5996bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                if (indexCount == 0 || !TextUtils.equals(title, currentTitle)) {
5997bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    titles[indexCount] = currentTitle = title;
5998bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount] = count;
5999bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    indexCount++;
6000bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                } else {
6001bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount - 1] += count;
6002bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                }
6003bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            }
6004bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
6005bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            if (indexCount < groupCount) {
6006bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String[] newTitles = new String[indexCount];
6007bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(titles, 0, newTitles, 0, indexCount);
6008bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                titles = newTitles;
6009bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
6010bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int[] newCounts = new int[indexCount];
6011bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(counts, 0, newCounts, 0, indexCount);
6012bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                counts = newCounts;
6013ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
6014ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6015409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            final Bundle bundle = new Bundle();
6016409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            bundle.putStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES, titles);
6017409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            bundle.putIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS, counts);
6018409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki
6019409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            ((AbstractCursor) cursor).setExtras(bundle);
6020409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            return cursor;
6021ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } finally {
6022f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            indexCursor.close();
6023ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
6024ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
6025ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
60262d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    /**
602792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Returns the contact Id for the contact identified by the lookupKey.
602892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Robust against changes in the lookup key: if the key has changed, will
602992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * look up the contact by the raw contact IDs or name encoded in the lookup
603092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * key.
60312d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     */
60322d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    public long lookupContactIdByLookupKey(SQLiteDatabase db, String lookupKey) {
60335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactLookupKey key = new ContactLookupKey();
60345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ArrayList<LookupKeySegment> segments = key.parse(lookupKey);
60355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
603692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        long contactId = -1;
60375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_PROFILE)) {
60385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            // We should already be in a profile database context, so just look up a single contact.
60395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro           contactId = lookupSingleContactId(db);
60405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
60415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
604292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_SOURCE_ID)) {
604392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdBySourceIds(db, segments);
604492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
604592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
604692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
604792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
604892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
604992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        boolean hasRawContactIds =
605092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID);
605192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds) {
605292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdByRawContactIds(db, segments);
605392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
605492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
605592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
605692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
605792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
605892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds
605992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                || lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME)) {
60605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            contactId = lookupContactIdByDisplayNames(db, segments);
60615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
60625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
60635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return contactId;
60645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
60655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
60665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private long lookupSingleContactId(SQLiteDatabase db) {
60675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = db.query(Tables.CONTACTS, new String[] {Contacts._ID},
60685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                null, null, null, null, null, "1");
60695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        try {
60705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (c.moveToFirst()) {
60715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return c.getLong(0);
60725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            } else {
60735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return -1;
60745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
60755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } finally {
60765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            c.close();
60775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
60785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
60795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
60805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupBySourceIdQuery {
608143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String TABLE = Views.RAW_CONTACTS;
60825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
60835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
60845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
608543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
60865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
60875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.SOURCE_ID
60885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
60895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
60905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
609143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int ACCOUNT_TYPE_AND_DATA_SET = 1;
60925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
60935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int SOURCE_ID = 3;
60945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
60955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
60965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdBySourceIds(SQLiteDatabase db,
60975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
60985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
60995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(RawContacts.SOURCE_ID + " IN (");
61005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
61015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
610292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID) {
61035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
61045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
61055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
61065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
61075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
61085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
61095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
61105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupBySourceIdQuery.TABLE, LookupBySourceIdQuery.COLUMNS,
61115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
61125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
61135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
611443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                String accountTypeAndDataSet =
611543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE_AND_DATA_SET);
61165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME);
61175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
611843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        ContactLookupKey.getAccountHashCode(accountTypeAndDataSet, accountName);
61195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID);
61205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
61215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
612292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID
612392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
61245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(sourceId)) {
61255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupBySourceIdQuery.CONTACT_ID);
61265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
61275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
61285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
61295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
61305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
61315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
61325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
61335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
61345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
61355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
61365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
613792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByRawContactIdQuery {
613843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String TABLE = Views.RAW_CONTACTS;
61395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
61405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
61415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
614243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
61435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
614492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts._ID,
61455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
61465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
61475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
614843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int ACCOUNT_TYPE_AND_DATA_SET = 1;
61495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
615092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ID = 3;
61515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
61525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
615392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByRawContactIds(SQLiteDatabase db,
615492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            ArrayList<LookupKeySegment> segments) {
615592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
615692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(RawContacts._ID + " IN (");
61575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
61585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
615992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
616092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(segment.rawContactId);
616192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(",");
61625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
61635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
616492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
616592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
61665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
616792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        Cursor c = db.query(LookupByRawContactIdQuery.TABLE, LookupByRawContactIdQuery.COLUMNS,
616892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                 sb.toString(), null, null, null, null);
616992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        try {
617092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            while (c.moveToNext()) {
617143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                String accountTypeAndDataSet = c.getString(
617243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        LookupByRawContactIdQuery.ACCOUNT_TYPE_AND_DATA_SET);
617392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountName = c.getString(LookupByRawContactIdQuery.ACCOUNT_NAME);
617492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                int accountHashCode =
617543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        ContactLookupKey.getAccountHashCode(accountTypeAndDataSet, accountName);
617692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String rawContactId = c.getString(LookupByRawContactIdQuery.ID);
617792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
617892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
617992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID
618092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
618192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && segment.rawContactId.equals(rawContactId)) {
618292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        segment.contactId = c.getLong(LookupByRawContactIdQuery.CONTACT_ID);
618392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        break;
618492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    }
618592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                }
618692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
618792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        } finally {
618892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            c.close();
61895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
61905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
619192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return getMostReferencedContactId(segments);
619292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
619392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
619492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByDisplayNameQuery {
619592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
619692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
619792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String COLUMNS[] = {
619892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.CONTACT_ID,
619943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
620092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
620192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME
620292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        };
620392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
620492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int CONTACT_ID = 0;
620543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int ACCOUNT_TYPE_AND_DATA_SET = 1;
620692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_NAME = 2;
620792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int NORMALIZED_NAME = 3;
620892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
620992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
621092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByDisplayNames(SQLiteDatabase db,
621192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
62125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
62135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(NameLookupColumns.NORMALIZED_NAME + " IN (");
62145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
62155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
621692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
621792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
62185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
62195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
62205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
62215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
62225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
62235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
62245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                + " AND " + RawContacts.CONTACT_ID + " NOT NULL");
62255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupByDisplayNameQuery.TABLE, LookupByDisplayNameQuery.COLUMNS,
62275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
62285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
62295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
623043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                String accountTypeAndDataSet =
623143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE_AND_DATA_SET);
62325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME);
62335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
623443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        ContactLookupKey.getAccountHashCode(accountTypeAndDataSet, accountName);
62355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME);
62365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
62375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
623892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if ((segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
623992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID)
624092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
62415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(name)) {
62425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupByDisplayNameQuery.CONTACT_ID);
62435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
62445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
62455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
62465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
62475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
62485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
62495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
62505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
62525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
62535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
625492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private boolean lookupKeyContainsType(ArrayList<LookupKeySegment> segments, int lookupType) {
625592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
625692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            LookupKeySegment segment = segments.get(i);
625792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == lookupType) {
625892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return true;
625992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
626092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
626192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
626292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return false;
626392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
626492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
6265ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateLookupKeyForRawContact(SQLiteDatabase db, long rawContactId) {
62665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().updateLookupKeyForRawContact(db, rawContactId);
6267ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    }
6268ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov
62695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /**
62705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     * Returns the contact ID that is mentioned the highest number of times.
62715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     */
62725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long getMostReferencedContactId(ArrayList<LookupKeySegment> segments) {
62735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Collections.sort(segments);
62745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long bestContactId = -1;
62765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int bestRefCount = 0;
62775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = -1;
62795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int count = 0;
62805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int segmentCount = segments.size();
62825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segmentCount; i++) {
62835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
62845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.contactId != -1) {
62855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segment.contactId == contactId) {
62865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count++;
62875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                } else {
62885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (count > bestRefCount) {
62895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestContactId = contactId;
62905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestRefCount = count;
62915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
62925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    contactId = segment.contactId;
62935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count = 1;
62945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
62955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
62965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
62975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count > bestRefCount) {
62985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return contactId;
62995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
63005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return bestContactId;
63015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
63025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
63035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
6304763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
6305763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            String[] projection) {
63064928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa        setTablesAndProjectionMapForContacts(qb, uri, projection, false);
63072f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    }
63082f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
63092f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    /**
63104928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * @param includeDataUsageStat true when the table should include DataUsageStat table.
63114928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * Note that this uses INNER JOIN instead of LEFT OUTER JOIN, so some of data in Contacts
63124928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * may be dropped.
63132f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     */
63142f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
63154928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            String[] projection, boolean includeDataUsageStat) {
631682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
6317ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.CONTACTS);
63182f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
63192f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa        // Just for frequently contacted contacts in Strequent Uri handling.
63204928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa        if (includeDataUsageStat) {
63212f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            sb.append(" INNER JOIN " +
6322ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                    Views.DATA_USAGE_STAT + " AS " + Tables.DATA_USAGE_STAT +
63232f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    " ON (" +
63242f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    DbQueryUtils.concatenateClauses(
63252f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                            DataUsageStatColumns.CONCRETE_TIMES_USED + " > 0",
63264928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            RawContacts.CONTACT_ID + "=" + Views.CONTACTS + "." + Contacts._ID) +
63272f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    ")");
63282f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa        }
63292f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
63307ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
63317ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
6332916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setTables(sb.toString());
6333916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionMap);
6334916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    }
6335916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
6336916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /**
6337916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * Finds name lookup records matching the supplied filter, picks one arbitrary match per
6338916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * contact and joins that with other contacts tables.
6339916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     */
6340916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private void setTablesAndProjectionMapForContactsWithSnippet(SQLiteQueryBuilder qb, Uri uri,
6341b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            String[] projection, String filter, long directoryId, boolean deferredSnippeting) {
63427ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov
63437ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
6344ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.CONTACTS);
6345916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
634603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        if (filter != null) {
634703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            filter = filter.trim();
634803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        }
634903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
635030cc766756461da8d53933f88ea01dd2272a90ebDmitri Plotnikov        if (TextUtils.isEmpty(filter) || (directoryId != -1 && directoryId != Directory.DEFAULT)) {
635130cc766756461da8d53933f88ea01dd2272a90ebDmitri Plotnikov            sb.append(" JOIN (SELECT NULL AS " + SearchSnippetColumns.SNIPPET + " WHERE 0)");
63525e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        } else {
6353b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            appendSearchIndexJoin(sb, uri, projection, filter, deferredSnippeting);
63545e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        }
63557ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
63567ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
635703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        qb.setTables(sb.toString());
635803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionWithSnippetMap);
635903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    }
6360916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
636103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private void appendSearchIndexJoin(
6362b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            StringBuilder sb, Uri uri, String[] projection, String filter,
6363b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            boolean  deferredSnippeting) {
6364916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
6365b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        if (snippetNeeded(projection)) {
636603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String[] args = null;
636703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String snippetArgs =
636803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                    getQueryParameter(uri, SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY);
636903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            if (snippetArgs != null) {
637003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                args = snippetArgs.split(",");
637103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
637203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
63735e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String startMatch = args != null && args.length > 0 ? args[0]
63745e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_START_MATCH;
63755e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String endMatch = args != null && args.length > 1 ? args[1]
63765e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_END_MATCH;
63775e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String ellipsis = args != null && args.length > 2 ? args[2]
63785e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_ELLIPSIS;
63795e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            int maxTokens = args != null && args.length > 3 ? Integer.parseInt(args[3])
63805e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_MAX_TOKENS;
63815e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
6382174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            appendSearchIndexJoin(
6383b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                    sb, filter, true, startMatch, endMatch, ellipsis, maxTokens,
6384b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                    deferredSnippeting);
6385174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        } else {
6386b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            appendSearchIndexJoin(sb, filter, false, null, null, null, 0, false);
6387174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
6388174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    }
6389174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
6390174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    public void appendSearchIndexJoin(StringBuilder sb, String filter,
6391174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            boolean snippetNeeded, String startMatch, String endMatch, String ellipsis,
6392b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            int maxTokens, boolean deferredSnippeting) {
6393174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        boolean isEmailAddress = false;
6394174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String emailAddress = null;
6395174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        boolean isPhoneNumber = false;
6396174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String phoneNumber = null;
6397174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String numberE164 = null;
6398174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
63993716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // If the query consists of a single word, we can do snippetizing after-the-fact for a
64003716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // performance boost.
6401b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        boolean singleTokenSearch = isSingleWordQuery(filter);
64023716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
6403174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (filter.indexOf('@') != -1) {
64045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            emailAddress = mDbHelper.get().extractAddressFromEmailAddress(filter);
6405174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            isEmailAddress = !TextUtils.isEmpty(emailAddress);
6406174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        } else {
6407174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            isPhoneNumber = isPhoneNumber(filter);
640804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            if (isPhoneNumber) {
640904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                phoneNumber = PhoneNumberUtils.normalizeNumber(filter);
641004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                numberE164 = PhoneNumberUtils.formatNumberToE164(phoneNumber,
64115d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mDbHelper.get().getCountryIso());
641204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            }
6413174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
6414174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
6415174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        sb.append(" JOIN (SELECT " + SearchIndexColumns.CONTACT_ID + " AS snippet_contact_id");
6416174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (snippetNeeded) {
64175e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            sb.append(", ");
64185e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            if (isEmailAddress) {
64193d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append("ifnull(");
64205e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, startMatch);
642104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("||(SELECT MIN(" + Email.ADDRESS + ")");
642204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" FROM " + Tables.DATA_JOIN_RAW_CONTACTS);
642304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" WHERE  " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
642404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + RawContacts.CONTACT_ID + " AND " + Email.ADDRESS + " LIKE ");
642504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                DatabaseUtils.appendEscapedSQLString(sb, filter + "%");
642604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(")||");
64273d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, endMatch);
64283d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append(",");
64293716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
6430b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                // Optimization for single-token search (do only if requested).
6431b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                if (singleTokenSearch && deferredSnippeting) {
64323716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    sb.append(SearchIndexColumns.CONTENT);
64333716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                } else {
64343716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
64353716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                }
64363d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append(")");
64373d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov            } else if (isPhoneNumber) {
64383d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append("ifnull(");
64393d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, startMatch);
644004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("||(SELECT MIN(" + Phone.NUMBER + ")");
644104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" FROM " +
644204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                        Tables.DATA_JOIN_RAW_CONTACTS + " JOIN " + Tables.PHONE_LOOKUP);
644304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" ON " + DataColumns.CONCRETE_ID);
644404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + Tables.PHONE_LOOKUP + "." + PhoneLookupColumns.DATA_ID);
644504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" WHERE  " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
644604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + RawContacts.CONTACT_ID);
644704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" AND " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
644804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(phoneNumber);
644904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("%'");
645004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                if (!TextUtils.isEmpty(numberE164)) {
645104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append(" OR " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
645204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append(numberE164);
645304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append("%'");
645404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                }
645504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(")||");
64565e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, endMatch);
64575e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                sb.append(",");
64583716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
6459b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                // Optimization for single-token search (do only if requested).
6460b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                if (singleTokenSearch && deferredSnippeting) {
64613716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    sb.append(SearchIndexColumns.CONTENT);
64623716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                } else {
64633716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
64643716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                }
64655e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                sb.append(")");
646603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            } else {
646704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                final String normalizedFilter = NameNormalizer.normalize(filter);
646804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                if (!TextUtils.isEmpty(normalizedFilter)) {
6469b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                    // Optimization for single-token search (do only if requested)..
6470b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                    if (singleTokenSearch && deferredSnippeting) {
64713716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(SearchIndexColumns.CONTENT);
64723716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    } else {
64733716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("(CASE WHEN EXISTS (SELECT 1 FROM ");
64743716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.RAW_CONTACTS + " AS rc INNER JOIN ");
64753716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.NAME_LOOKUP + " AS nl ON (rc." + RawContacts._ID);
64763716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("=nl." + NameLookupColumns.RAW_CONTACT_ID);
64773716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(") WHERE nl." + NameLookupColumns.NORMALIZED_NAME);
64783716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(" GLOB '" + normalizedFilter + "*' AND ");
64793716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("nl." + NameLookupColumns.NAME_TYPE + "=");
64803716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(NameLookupType.NAME_COLLATION_KEY + " AND ");
64813716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
64823716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("=rc." + RawContacts.CONTACT_ID);
64833716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(") THEN NULL ELSE ");
64843716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
64853716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(" END)");
64863716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    }
648704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                } else {
648804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append("NULL");
648904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                }
649003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
64915e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            sb.append(" AS " + SearchSnippetColumns.SNIPPET);
64925e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        }
649303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
64945e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(" FROM " + Tables.SEARCH_INDEX);
64955e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(" WHERE ");
64965e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(Tables.SEARCH_INDEX + " MATCH ");
64975e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        if (isEmailAddress) {
64982352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb, "\"" + sanitizeMatch(filter) + "*\"");
64993d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov        } else if (isPhoneNumber) {
65002352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb,
650104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    "\"" + sanitizeMatch(filter) + "*\" OR \"" + phoneNumber + "*\""
65022352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                            + (numberE164 != null ? " OR \"" + numberE164 + "\"" : ""));
650303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        } else {
65042352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filter) + "*");
65059c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        }
650603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        sb.append(") ON (" + Contacts._ID + "=snippet_contact_id)");
6507a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov    }
6508a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov
65092352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov    private String sanitizeMatch(String filter) {
65102352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov        // TODO more robust preprocessing of match expressions
65112352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov        return filter.replace('-', ' ').replace('\"', ' ');
65122352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov    }
65132352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov
65145e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov    private void appendSnippetFunction(
65155e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            StringBuilder sb, String startMatch, String endMatch, String ellipsis, int maxTokens) {
65165e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append("snippet(" + Tables.SEARCH_INDEX + ",");
65175e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, startMatch);
65185e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",");
65195e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, endMatch);
65205e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",");
65215e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, ellipsis);
65225e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
65235e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        // The index of the column used for the snippet, "content"
65245e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",1,");
65255e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(maxTokens);
65265e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(")");
65275e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov    }
65285e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
6529763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForRawContacts(SQLiteQueryBuilder qb, Uri uri) {
6530763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        StringBuilder sb = new StringBuilder();
6531ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.RAW_CONTACTS);
6532763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setTables(sb.toString());
6533763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setProjectionMap(sRawContactsProjectionMap);
6534f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        appendAccountFromParameter(qb, uri);
6535763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    }
6536763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
6537a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForRawEntities(SQLiteQueryBuilder qb, Uri uri) {
6538ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        qb.setTables(Views.RAW_ENTITIES);
6539a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sRawEntityProjectionMap);
6540f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        appendAccountFromParameter(qb, uri);
654146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    }
654246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
654382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
654482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String[] projection, boolean distinct) {
654546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        setTablesAndProjectionMapForData(qb, uri, projection, distinct, null);
654646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
654746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
654846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
654946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * @param usageType when non-null {@link Tables#DATA_USAGE_STAT} is joined with the specified
655046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * type.
655146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
655246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
655346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            String[] projection, boolean distinct, Integer usageType) {
655482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
6555ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.DATA);
655682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sb.append(" data");
655782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
6558a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, RawContacts.CONTACT_ID);
6559a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
6560a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
6561a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
65623296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
656346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (usageType != null) {
656446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            appendDataUsageStatJoin(sb, usageType, DataColumns.CONCRETE_ID);
656546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
656646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
656782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
6568f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov
6569f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        boolean useDistinct = distinct
65705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                || !mDbHelper.get().isInProjection(projection, DISTINCT_DATA_PROHIBITING_COLUMNS);
6571f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setDistinct(useDistinct);
6572f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setProjectionMap(useDistinct ? sDistinctDataProjectionMap : sDataProjectionMap);
6573f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        appendAccountFromParameter(qb, uri);
6574ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    }
6575ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
65760a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb,
65770a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String[] projection) {
65780a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        StringBuilder sb = new StringBuilder();
6579ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.DATA);
65800a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        sb.append(" data");
6581a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
6582a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
65830a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
6584a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
6585a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sStatusUpdatesProjectionMap);
6586a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6587a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
65883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void setTablesAndProjectionMapForStreamItems(SQLiteQueryBuilder qb) {
65899b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann        qb.setTables(Views.STREAM_ITEMS);
65903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        qb.setProjectionMap(sStreamItemsProjectionMap);
65913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
65923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
65933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void setTablesAndProjectionMapForStreamItemPhotos(SQLiteQueryBuilder qb) {
65941dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro        qb.setTables(Tables.PHOTO_FILES
65951dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + " JOIN " + Tables.STREAM_ITEM_PHOTOS + " ON ("
65961dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + StreamItemPhotosColumns.CONCRETE_PHOTO_FILE_ID + "="
65971dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + PhotoFilesColumns.CONCRETE_ID
65981dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + ") JOIN " + Tables.STREAM_ITEMS + " ON ("
65991dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "="
66000bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                + StreamItemsColumns.CONCRETE_ID + ")"
66010bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                + " JOIN " + Tables.RAW_CONTACTS + " ON ("
66020bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                + StreamItemsColumns.CONCRETE_RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID
66030bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                + ")");
66043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        qb.setProjectionMap(sStreamItemPhotosProjectionMap);
66053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
66063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
6607a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForEntities(SQLiteQueryBuilder qb, Uri uri,
6608a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection) {
6609a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
6610ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.ENTITIES);
6611a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(" data");
6612a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6613a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts.Entity.CONTACT_ID);
6614a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
6615a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, Contacts.Entity.DATA_ID);
6616a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, Contacts.Entity.DATA_ID);
6617a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6618a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
6619a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sEntityProjectionMap);
6620f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        appendAccountFromParameter(qb, uri);
6621a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6622a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6623a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactStatusUpdateJoin(StringBuilder sb, String[] projection,
6624a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String lastStatusUpdateIdColumn) {
66255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mDbHelper.get().isInProjection(projection,
6626a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS,
6627a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
6628a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
6629a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
6630a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP)) {
6631a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
6632a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    + ContactsStatusUpdatesColumns.ALIAS +
6633a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + lastStatusUpdateIdColumn + "="
6634a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
66350a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
6636a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
66370a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
6638a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataStatusUpdateJoin(StringBuilder sb, String[] projection,
6639a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
66405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mDbHelper.get().isInProjection(projection,
66410a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS,
66420a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_RES_PACKAGE,
66430a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_ICON,
66440a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_LABEL,
66450a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_TIMESTAMP)) {
66460a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
6647a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdatesColumns.CONCRETE_DATA_ID + "="
6648a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + dataIdColumn + ")");
66490a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
6650a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6651a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
665246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private void appendDataUsageStatJoin(StringBuilder sb, int usageType, String dataIdColumn) {
665346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        sb.append(" LEFT OUTER JOIN " + Tables.DATA_USAGE_STAT +
665446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "=" + dataIdColumn +
665546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                " AND " + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "=" + usageType + ")");
665646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
665746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
6658a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactPresenceJoin(StringBuilder sb, String[] projection,
6659a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn) {
66605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mDbHelper.get().isInProjection(projection,
6661a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_PRESENCE, Contacts.CONTACT_CHAT_CAPABILITY)) {
6662a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
6663a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + contactIdColumn + " = "
6664a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + AggregatedPresenceColumns.CONCRETE_CONTACT_ID + ")");
6665a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
6666a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6667a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6668a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataPresenceJoin(StringBuilder sb, String[] projection,
6669a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
66705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mDbHelper.get().isInProjection(projection, Data.PRESENCE, Data.CHAT_CAPABILITY)) {
6671a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
6672a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdates.DATA_ID + "=" + dataIdColumn + ")");
6673a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
6674a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6675a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
667624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private boolean appendLocalDirectorySelectionIfNeeded(SQLiteQueryBuilder qb, long directoryId) {
6677385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directoryId == Directory.DEFAULT) {
6678385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " IN " + Tables.DEFAULT_DIRECTORY);
667924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            return true;
6680385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directoryId == Directory.LOCAL_INVISIBLE){
6681385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " NOT IN " + Tables.DEFAULT_DIRECTORY);
668224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            return true;
668324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
668424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        return false;
668524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
668624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
6687f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro    private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) {
6688f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
6689f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
669043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        final String dataSet = getQueryParameter(uri, RawContacts.DATA_SET);
6691e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6692e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
6693e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
6694e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
66955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
6696fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
6697e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
6698e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6699e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
6700e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
6701e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
6702e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
670343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            String toAppend = RawContacts.ACCOUNT_NAME + "="
67044a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
67054a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
670643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + DatabaseUtils.sqlEscapeString(accountType);
6707f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            if (dataSet == null) {
6708f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                toAppend += " AND " + RawContacts.DATA_SET + " IS NULL";
6709f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            } else {
6710f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                toAppend += " AND " + RawContacts.DATA_SET + "=" +
6711f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                        DatabaseUtils.sqlEscapeString(dataSet);
671243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
671343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            qb.appendWhere(toAppend);
67144a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        } else {
67154a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere("1");
67164a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
67174a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
67184a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
6719e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    private String appendAccountToSelection(Uri uri, String selection) {
6720f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
6721f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
672243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        final String dataSet = getQueryParameter(uri, RawContacts.DATA_SET);
6723e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6724e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
6725e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
6726e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
67275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
6728fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
6729e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
6730e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6731e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
6732e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
6733e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
6734e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
6735e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "="
6736e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
6737e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + RawContacts.ACCOUNT_TYPE + "="
6738e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountType));
6739f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            if (dataSet == null) {
6740f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                selectionSb.append(" AND " + RawContacts.DATA_SET + " IS NULL");
6741f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            } else {
674243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                selectionSb.append(" AND " + RawContacts.DATA_SET + "=")
674343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        .append(DatabaseUtils.sqlEscapeString(dataSet));
674443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
6745e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (!TextUtils.isEmpty(selection)) {
6746e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(" AND (");
6747e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(selection);
6748e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(')');
6749e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
6750e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selectionSb.toString();
6751e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } else {
6752e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selection;
6753e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
6754e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    }
6755e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
67567e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
6757c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
6758c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *
6759c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
6760c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
6761c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     */
6762f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private String getLimit(Uri uri) {
67632e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limitParam = getQueryParameter(uri, ContactsContract.LIMIT_PARAM_KEY);
6764c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limitParam == null) {
6765c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
6766c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
6767c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // make sure that the limit is a non-negative integer
6768c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        try {
6769c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int l = Integer.parseInt(limitParam);
6770c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (l < 0) {
6771c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
6772c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return null;
6773c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
6774c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return String.valueOf(l);
6775c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        } catch (NumberFormatException ex) {
6776c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
6777c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
6778c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
6779c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
6780c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
6781b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    @Override
6782f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
6783f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (mode.equals("r")) {
6784f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            waitForAccess(mReadAccessLatch);
6785f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } else {
6786f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            waitForAccess(mWriteAccessLatch);
6787f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
67885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDb(uri)) {
67895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToProfileMode();
67905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return mProfileProvider.openAssetFile(uri, mode);
67915d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } else {
67925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToContactMode();
67935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (mode.equals("r")) {
67945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mDb = mDbHelper.get().getReadableDatabase();
67955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            } else {
67965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mDb = mDbHelper.get().getWritableDatabase();
67975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
67985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return openAssetFileLocal(uri, mode);
67995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
68005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
68015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
68025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    public AssetFileDescriptor openAssetFileLocal(Uri uri, String mode)
68035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            throws FileNotFoundException {
68045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
68055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
68065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
68075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.set(mDb);
68085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
6809415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
6810b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        int match = sUriMatcher.match(uri);
6811b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        switch (match) {
6812a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
6813bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                long contactId = Long.parseLong(uri.getPathSegments().get(1));
68145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return openPhotoAssetFile(mActiveDb.get(), uri, mode,
681524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        Data._ID + "=" + Contacts.PHOTO_ID + " AND " +
681624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                                RawContacts.CONTACT_ID + "=?",
6817bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        new String[]{String.valueOf(contactId)});
6818e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            }
6819b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
6820f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_ID_DISPLAY_PHOTO: {
6821f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
6822f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
6823f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Display photos retrieved by contact ID can only be read.");
6824f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6825f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long contactId = Long.parseLong(uri.getPathSegments().get(1));
68265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = mActiveDb.get().query(Tables.CONTACTS,
6827f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{Contacts.PHOTO_FILE_ID},
6828f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        Contacts._ID + "=?", new String[]{String.valueOf(contactId)},
6829f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        null, null, null);
6830f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
6831f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.moveToFirst();
6832f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    long photoFileId = c.getLong(0);
6833f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForRead(photoFileId);
6834f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
6835f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
6836f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6837f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6838f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6839bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_PHOTO:
6840bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_ID_PHOTO:
6841f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_DISPLAY_PHOTO:
6842f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_ID_DISPLAY_PHOTO: {
6843f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
6844f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
6845bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                            "Photos retrieved by contact lookup key can only be read.");
6846f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6847f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                List<String> pathSegments = uri.getPathSegments();
6848f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                int segmentCount = pathSegments.size();
6849f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (segmentCount < 4) {
68505d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
6851f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Missing a lookup key", uri));
6852f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6853bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro
6854bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                boolean forDisplayPhoto = (match == CONTACTS_LOOKUP_ID_DISPLAY_PHOTO
6855bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        || match == CONTACTS_LOOKUP_DISPLAY_PHOTO);
6856f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                String lookupKey = pathSegments.get(2);
6857bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                String[] projection = new String[]{Contacts.PHOTO_ID, Contacts.PHOTO_FILE_ID};
6858f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (segmentCount == 5) {
6859f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    long contactId = Long.parseLong(pathSegments.get(3));
6860f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
6861f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
68625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
6863f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            projection, null, null, null, null, null,
6864f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
6865f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (c != null) {
6866f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        try {
6867f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            c.moveToFirst();
6868bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                            if (forDisplayPhoto) {
6869bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                long photoFileId =
6870bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                        c.getLong(c.getColumnIndex(Contacts.PHOTO_FILE_ID));
6871bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                return openDisplayPhotoForRead(photoFileId);
6872bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                            } else {
6873bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                long photoId = c.getLong(c.getColumnIndex(Contacts.PHOTO_ID));
6874bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                return openPhotoAssetFile(mActiveDb.get(), uri, mode,
6875bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                        Data._ID + "=?", new String[]{String.valueOf(photoId)});
6876bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                            }
6877f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        } finally {
6878f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            c.close();
6879f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
6880f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
6881f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6882f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6883f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
6884f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                setTablesAndProjectionMapForContacts(qb, uri, projection);
68855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
68865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = qb.query(mActiveDb.get(), projection, Contacts._ID + "=?",
6887f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{String.valueOf(contactId)}, null, null, null);
6888f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
6889f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.moveToFirst();
6890bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    if (forDisplayPhoto) {
6891bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        long photoFileId = c.getLong(c.getColumnIndex(Contacts.PHOTO_FILE_ID));
6892bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        return openDisplayPhotoForRead(photoFileId);
6893bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    } else {
6894bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        long photoId = c.getLong(c.getColumnIndex(Contacts.PHOTO_ID));
6895bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        return openPhotoAssetFile(mActiveDb.get(), uri, mode,
6896bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                Data._ID + "=?", new String[]{String.valueOf(photoId)});
6897bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    }
6898f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
6899f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
6900f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6901f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6902f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6903f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case RAW_CONTACTS_ID_DISPLAY_PHOTO: {
6904f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
6905f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                boolean writeable = !mode.equals("r");
6906f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6907f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Find the primary photo data record for this raw contact.
6908f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
6909f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                String[] projection = new String[]{Data._ID, Photo.PHOTO_FILE_ID};
6910f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
69117cf50494501938f175d288077145acf49da8f171Daniel Lehmann                long photoMimetypeId = mDbHelper.get().getMimeTypeId(Photo.CONTENT_ITEM_TYPE);
69125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = qb.query(mActiveDb.get(), projection,
69137cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        Data.RAW_CONTACT_ID + "=? AND " + DataColumns.MIMETYPE_ID + "=?",
69147cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        new String[]{String.valueOf(rawContactId), String.valueOf(photoMimetypeId)},
6915f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        null, null, Data.IS_PRIMARY + " DESC");
6916f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long dataId = 0;
6917f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long photoFileId = 0;
6918f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
6919f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (c.getCount() >= 1) {
6920f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        c.moveToFirst();
6921f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        dataId = c.getLong(0);
6922f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        photoFileId = c.getLong(1);
6923f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
6924f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
6925f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
6926f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6927f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6928f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // If writeable, open a writeable file descriptor that we can monitor.
6929f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // When the caller finishes writing content, we'll process the photo and
6930f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // update the data record.
6931f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (writeable) {
6932f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForWrite(rawContactId, dataId, uri, mode);
6933f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } else {
6934f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForRead(photoFileId);
6935f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6936f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6937f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6938f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case DISPLAY_PHOTO: {
6939f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long photoFileId = ContentUris.parseId(uri);
6940f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
6941f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
6942f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Display photos retrieved by key can only be read.");
6943f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6944f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                return openDisplayPhotoForRead(photoFileId);
6945f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6946f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6947e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            case DATA_ID: {
694824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long dataId = Long.parseLong(uri.getPathSegments().get(1));
69497cf50494501938f175d288077145acf49da8f171Daniel Lehmann                long photoMimetypeId = mDbHelper.get().getMimeTypeId(Photo.CONTENT_ITEM_TYPE);
69505d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return openPhotoAssetFile(mActiveDb.get(), uri, mode,
69517cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        Data._ID + "=? AND " + DataColumns.MIMETYPE_ID + "=" + photoMimetypeId,
695224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        new String[]{String.valueOf(dataId)});
6953d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
6954d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6955fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            case PROFILE_AS_VCARD: {
6956fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // When opening a contact as file, we pass back contents as a
6957fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // vCard-encoded stream. We build into a local buffer first,
6958fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // then pipe into MemoryFile once the exact size is known.
6959fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
6960fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(uri, localStream, null, null);
6961fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                return buildAssetFileDescriptor(localStream);
6962fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            }
696342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
6964fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            case CONTACTS_AS_VCARD: {
696542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // When opening a contact as file, we pass back contents as a
696642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // vCard-encoded stream. We build into a local buffer first,
696742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // then pipe into MemoryFile once the exact size is known.
696842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
6969fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(uri, localStream, null, null);
6970f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
697142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
697242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
697342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
697442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKeys = uri.getPathSegments().get(2);
697542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String[] loopupKeyList = lookupKeys.split(":");
697642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final StringBuilder inBuilder = new StringBuilder();
6977fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                Uri queryUri = Contacts.CONTENT_URI;
697842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                int index = 0;
6979fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen
6980d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // SQLite has limits on how many parameters can be used
6981d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // so the IDs are concatenated to a query string here instead
698242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                for (String lookupKey : loopupKeyList) {
698342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    if (index == 0) {
6984d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append("(");
698542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    } else {
6986d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append(",");
698742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    }
69885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    // TODO: Figure out what to do if the profile contact is in the list.
69895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
699024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    inBuilder.append(contactId);
699142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    index++;
699242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                }
699342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                inBuilder.append(')');
699442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + " IN " + inBuilder.toString();
6995d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6996d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // When opening a contact as file, we pass back contents as a
6997d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // vCard-encoded stream. We build into a local buffer first,
6998d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // then pipe into MemoryFile once the exact size is known.
6999d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
7000fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(queryUri, localStream, selection, null);
7001f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
7002d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
7003b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
7004b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            default:
70055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                throw new FileNotFoundException(mDbHelper.get().exceptionMessage(
70065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        "File does not exist", uri));
7007b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        }
7008b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
7009b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
7010afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro    private AssetFileDescriptor openPhotoAssetFile(SQLiteDatabase db, Uri uri, String mode,
7011afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro            String selection, String[] selectionArgs)
7012e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throws FileNotFoundException {
7013e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        if (!"r".equals(mode)) {
70145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            throw new FileNotFoundException(mDbHelper.get().exceptionMessage("Mode " + mode
7015e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                    + " not supported.", uri));
7016e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        }
7017e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
7018e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        String sql =
7019ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                "SELECT " + Photo.PHOTO + " FROM " + Views.DATA +
7020e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                " WHERE " + selection;
702108ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        try {
7022f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
7023f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    DatabaseUtils.blobFileDescriptorForQuery(db, sql, selectionArgs));
702408ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        } catch (SQLiteDoneException e) {
702508ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            // this will happen if the DB query returns no rows (i.e. contact does not exist)
702608ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            throw new FileNotFoundException(uri.toString());
702708ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        }
7028e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov    }
7029e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
7030f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
7031f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Opens a display photo from the photo store for reading.
7032f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param photoFileId The display photo file ID
7033f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @return An asset file descriptor that allows the file to be read.
7034f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @throws FileNotFoundException If no photo file for the given ID exists.
7035f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
7036f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private AssetFileDescriptor openDisplayPhotoForRead(long photoFileId)
7037f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            throws FileNotFoundException {
70385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        PhotoStore.Entry entry = mPhotoStore.get().get(photoFileId);
7039f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (entry != null) {
7040f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            return makeAssetFileDescriptor(
7041f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    ParcelFileDescriptor.open(new File(entry.path),
7042f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            ParcelFileDescriptor.MODE_READ_ONLY),
7043f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    entry.size);
7044f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } else {
7045f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
7046f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            throw new FileNotFoundException("No photo file found for ID " + photoFileId);
7047f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
7048f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
7049f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7050f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
7051f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Opens a file descriptor for a photo to be written.  When the caller completes writing
7052f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * to the file (closing the output stream), the image will be parsed out and processed.
7053f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * If processing succeeds, the given raw contact ID's primary photo record will be
7054f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * populated with the inserted image (if no primary photo record exists, the data ID can
7055f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * be left as 0, and a new data record will be inserted).
7056f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param rawContactId Raw contact ID this photo entry should be associated with.
7057f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param dataId Data ID for a photo mimetype that will be updated with the inserted
7058f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     image.  May be set to 0, in which case the inserted image will trigger creation
7059f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     of a new primary photo image data row for the raw contact.
7060f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param uri The URI being used to access this file.
7061f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param mode Read/write mode string.
7062f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @return An asset file descriptor the caller can use to write an image file for the
7063f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     raw contact.
7064f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
7065f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private AssetFileDescriptor openDisplayPhotoForWrite(long rawContactId, long dataId, Uri uri,
7066f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            String mode) {
7067f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        try {
7068c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            ParcelFileDescriptor[] pipeFds = ParcelFileDescriptor.createPipe();
7069c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            PipeMonitor pipeMonitor = new PipeMonitor(rawContactId, dataId, pipeFds[0]);
7070c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            pipeMonitor.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Object[]) null);
7071c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            return new AssetFileDescriptor(pipeFds[1], 0, AssetFileDescriptor.UNKNOWN_LENGTH);
7072f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } catch (IOException ioe) {
7073f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            Log.e(TAG, "Could not create temp image file in mode " + mode);
7074f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            return null;
7075f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
7076f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
7077f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7078f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
7079c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro     * Async task that monitors the given file descriptor (the read end of a pipe) for
7080c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro     * the writer finishing.  If the data from the pipe contains a valid image, the image
7081c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro     * is either inserted into the given raw contact or updated in the given data row.
7082f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
7083c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro    private class PipeMonitor extends AsyncTask<Object, Object, Object> {
7084c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro        private final ParcelFileDescriptor mDescriptor;
7085f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        private final long mRawContactId;
7086f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        private final long mDataId;
7087c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro        private PipeMonitor(long rawContactId, long dataId, ParcelFileDescriptor descriptor) {
7088f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            mRawContactId = rawContactId;
7089f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            mDataId = dataId;
7090c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            mDescriptor = descriptor;
7091f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
7092f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7093f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        @Override
7094c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro        protected Object doInBackground(Object... params) {
7095c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            AutoCloseInputStream is = new AutoCloseInputStream(mDescriptor);
7096f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            try {
7097c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                Bitmap b = BitmapFactory.decodeStream(is);
7098f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (b != null) {
7099fa4db3db4146a26f154ef2e89352ad70a5415b8eDaniel Lehmann                    waitForAccess(mWriteAccessLatch);
7100f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    PhotoProcessor processor = new PhotoProcessor(b, mMaxDisplayPhotoDim,
7101f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            mMaxThumbnailPhotoDim);
7102f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7103f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    // Store the compressed photo in the photo store.
71045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    PhotoStore photoStore = ContactsContract.isProfileId(mRawContactId)
71055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            ? mProfilePhotoStore
71065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            : mContactsPhotoStore;
71075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    long photoFileId = photoStore.insert(processor);
7108f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7109c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                    // Depending on whether we already had a data row to attach the photo
7110c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                    // to, do an update or insert.
7111f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (mDataId != 0) {
7112f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Update the data record with the new photo.
7113f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        ContentValues updateValues = new ContentValues();
7114f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7115f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Signal that photo processing has already been handled.
7116f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        updateValues.put(DataRowHandlerForPhoto.SKIP_PROCESSING_KEY, true);
7117f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7118f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        if (photoFileId != 0) {
7119f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            updateValues.put(Photo.PHOTO_FILE_ID, photoFileId);
7120f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
7121f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        updateValues.put(Photo.PHOTO, processor.getThumbnailPhotoBytes());
7122c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                        update(ContentUris.withAppendedId(Data.CONTENT_URI, mDataId),
7123c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                                updateValues, null, null);
7124f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    } else {
7125f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Insert a new primary data record with the photo.
7126f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        ContentValues insertValues = new ContentValues();
7127f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7128f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Signal that photo processing has already been handled.
7129f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(DataRowHandlerForPhoto.SKIP_PROCESSING_KEY, true);
7130f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7131f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
7132f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Data.IS_PRIMARY, 1);
7133f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        if (photoFileId != 0) {
7134f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            insertValues.put(Photo.PHOTO_FILE_ID, photoFileId);
7135f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
7136f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Photo.PHOTO, processor.getThumbnailPhotoBytes());
7137f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insert(RawContacts.CONTENT_URI.buildUpon()
7138f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                .appendPath(String.valueOf(mRawContactId))
7139f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                .appendPath(RawContacts.Data.CONTENT_DIRECTORY).build(),
7140f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                insertValues);
7141f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
7142c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro
7143f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7144c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            } catch (IOException e) {
7145c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                throw new RuntimeException(e);
7146f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
7147c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            return null;
7148f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
7149f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
7150f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7151d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String CONTACT_MEMORY_FILE_NAME = "contactAssetFile";
7152d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7153d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
7154f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert     * Returns an {@link AssetFileDescriptor} backed by the
7155d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * contents of the given {@link ByteArrayOutputStream}.
7156d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
7157f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor buildAssetFileDescriptor(ByteArrayOutputStream stream) {
7158d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        try {
7159d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            stream.flush();
7160d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7161d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final byte[] byteData = stream.toByteArray();
7162d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7163f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
7164f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    ParcelFileDescriptor.fromData(byteData, CONTACT_MEMORY_FILE_NAME),
7165f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    byteData.length);
7166d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        } catch (IOException e) {
7167ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            Log.w(TAG, "Problem writing stream into an ParcelFileDescriptor: " + e.toString());
7168ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            return null;
7169d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
7170d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
7171d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7172f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd) {
7173f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return makeAssetFileDescriptor(fd, AssetFileDescriptor.UNKNOWN_LENGTH);
7174f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
7175f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
7176f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd, long length) {
7177f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return fd != null ? new AssetFileDescriptor(fd, 0, length) : null;
7178f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
7179f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
7180d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
7181d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Output {@link RawContacts} matching the requested selection in the vCard
7182d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * format to the given {@link OutputStream}. This method returns silently if
7183d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * any errors encountered.
7184d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
7185fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen    private void outputRawContactsAsVCard(Uri uri, OutputStream stream,
7186fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            String selection, String[] selectionArgs) {
7187d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final Context context = this.getContext();
7188dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen        int vcardconfig = VCardConfig.VCARD_TYPE_DEFAULT;
7189fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen        if(uri.getBooleanQueryParameter(
7190fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                Contacts.QUERY_PARAMETER_VCARD_NO_PHOTO, false)) {
7191dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen            vcardconfig |= VCardConfig.FLAG_REFRAIN_IMAGE_EXPORT;
7192dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen        }
71937a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        final VCardComposer composer =
7194dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen                new VCardComposer(context, vcardconfig, false);
7195108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        Writer writer = null;
71963711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen        final Uri rawContactsUri;
71973711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen        if (mapsToProfileDb(uri)) {
71983711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen            rawContactsUri = RawContactsEntity.PROFILE_CONTENT_URI;
71993711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen        } else {
72003711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen            rawContactsUri = RawContactsEntity.CONTENT_URI;
72013711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen        }
7202108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        try {
7203108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            writer = new BufferedWriter(new OutputStreamWriter(stream));
72043711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen            if (!composer.init(uri, selection, selectionArgs, null, rawContactsUri)) {
7205108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                Log.w(TAG, "Failed to init VCardComposer");
7206108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                return;
7207108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            }
7208d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7209108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            while (!composer.isAfterLast()) {
7210108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                writer.write(composer.createOneEntry());
7211108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            }
7212108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        } catch (IOException e) {
7213108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            Log.e(TAG, "IOException: " + e);
7214108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        } finally {
7215108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            composer.terminate();
7216108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            if (writer != null) {
7217108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                try {
7218108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                    writer.close();
7219108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                } catch (IOException e) {
7220108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                    Log.w(TAG, "IOException during closing output stream: " + e);
7221108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                }
7222d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
7223d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
7224d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
7225b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
72264f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
72274f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
7228415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
7229415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov        waitForAccess(mReadAccessLatch);
7230415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
7231a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
72324f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
7233b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS:
7234be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return Contacts.CONTENT_TYPE;
72352d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill            case CONTACTS_LOOKUP:
7236b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_ID:
7237b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
723824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE:
7239b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_ITEM_TYPE;
7240f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD:
724142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD:
724224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD:
7243f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                return Contacts.CONTENT_VCARD_TYPE;
7244f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            case CONTACTS_ID_PHOTO:
7245bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_PHOTO:
7246bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_ID_PHOTO:
7247f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_ID_DISPLAY_PHOTO:
7248f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_DISPLAY_PHOTO:
7249f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_ID_DISPLAY_PHOTO:
7250f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case RAW_CONTACTS_ID_DISPLAY_PHOTO:
7251f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case DISPLAY_PHOTO:
7252f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                return "image/jpeg";
7253b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS:
725424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS:
7255be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return RawContacts.CONTENT_TYPE;
7256b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS_ID:
725724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID:
7258b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return RawContacts.CONTENT_ITEM_TYPE;
7259f481f22a9323fe338672f99b88b26c5f0725cd42David Brown            case DATA:
726024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA:
7261f481f22a9323fe338672f99b88b26c5f0725cd42David Brown                return Data.CONTENT_TYPE;
7262508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
72635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long id = ContentUris.parseId(uri);
72645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                if (ContactsContract.isProfileId(id)) {
72655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    return mProfileHelper.getDataMimeType(id);
72665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                } else {
72675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    return mContactsHelper.getDataMimeType(id);
72685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                }
726948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES:
727048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_TYPE;
727148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
727248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_ITEM_TYPE;
72739005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov            case PHONE_LOOKUP:
72749005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov                return PhoneLookup.CONTENT_TYPE;
727548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS:
727648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_TYPE;
727748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
727848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_ITEM_TYPE;
727948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS:
728048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_TYPE;
728148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID:
728248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_ITEM_TYPE;
7283b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
7284b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_TYPE;
7285b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
7286b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_ITEM_TYPE;
7287b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case SETTINGS:
7288b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Settings.CONTENT_TYPE;
7289b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
7290b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_TYPE;
7291c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS:
7292c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SUGGEST_MIME_TYPE;
7293c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT:
7294c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
7295d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES:
7296d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_TYPE;
7297d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID:
7298d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_ITEM_TYPE;
7299af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS:
7300af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                return StreamItems.CONTENT_TYPE;
7301af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS_ID:
7302af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                return StreamItems.CONTENT_ITEM_TYPE;
7303af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS_ID_PHOTOS:
7304af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                return StreamItems.StreamItemPhotos.CONTENT_TYPE;
7305af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS_ID_PHOTOS_ID:
7306af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                return StreamItems.StreamItemPhotos.CONTENT_ITEM_TYPE;
7307af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS_PHOTOS:
7308af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                throw new UnsupportedOperationException("Not supported for write-only URI " + uri);
730961efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov            default:
731061efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov                return mLegacyApiSupport.getType(uri);
73114f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
73124f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
73137e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
731409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    public String[] getDefaultProjection(Uri uri) {
731509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        final int match = sUriMatcher.match(uri);
731609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        switch (match) {
731709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS:
731809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP:
731909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_ID:
732009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
732109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
732224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE:
732309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsProjectionMap.getColumnNames();
732409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
73258727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov            case CONTACTS_ID_ENTITIES:
732624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_ENTITIES:
73278727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov                return sEntityProjectionMap.getColumnNames();
73288727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov
732909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_VCARD:
733009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_MULTI_VCARD:
733124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD:
733209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsVCardProjectionMap.getColumnNames();
733309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
733409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS:
733509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS_ID:
733624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS:
733724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID:
733809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sRawContactsProjectionMap.getColumnNames();
733909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
734009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DATA_ID:
734109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES:
734209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES_ID:
734309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS:
734409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS_ID:
734509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS:
734609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS_ID:
734724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA:
734809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDataProjectionMap.getColumnNames();
734909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
735009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONE_LOOKUP:
735109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sPhoneLookupProjectionMap.getColumnNames();
735209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
735309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
735409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
735509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sAggregationExceptionsProjectionMap.getColumnNames();
735609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
735709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case SETTINGS:
735809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sSettingsProjectionMap.getColumnNames();
735909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
736009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES:
736109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES_ID:
736209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDirectoryProjectionMap.getColumnNames();
736309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
736409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            default:
736509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return null;
736609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
736709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    }
736809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
7369f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private class StructuredNameLookupBuilder extends NameLookupBuilder {
7370f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
7371f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public StructuredNameLookupBuilder(NameSplitter splitter) {
7372f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            super(splitter);
7373f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
7374f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
7375f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
7376f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
7377f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name) {
73785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mDbHelper.get().insertNameLookup(rawContactId, dataId, lookupType, name);
7379f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
7380f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
7381f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
7382f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected String[] getCommonNicknameClusters(String normalizedName) {
7383d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov            return mCommonNicknameCache.getCommonNicknameClusters(normalizedName);
7384f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
7385f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
7386f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
73872d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov    public void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
7388d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
7389d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + RawContacts.CONTACT_ID +
7390d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.RAW_CONTACTS +
7391d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " JOIN " + Tables.NAME_LOOKUP +
7392d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " ON(" + RawContactsColumns.CONCRETE_ID + "="
7393d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
7394d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE normalized_name GLOB '");
7395e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
7396916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE +
7397916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " IN(" + CONTACT_LOOKUP_NAME_TYPES + "))");
7398e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    }
7399e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
74009a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    public boolean isPhoneNumber(String filter) {
74019a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        boolean atLeastOneDigit = false;
74029a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        int len = filter.length();
74039a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        for (int i = 0; i < len; i++) {
74049a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            char c = filter.charAt(i);
74059a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            if (c >= '0' && c <= '9') {
74069a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                atLeastOneDigit = true;
74079a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            } else if (c != '*' && c != '#' && c != '+' && c != 'N' && c != '.' && c != ';'
74089a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                    && c != '-' && c != '(' && c != ')' && c != ' ') {
74099a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                return false;
74109a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            }
74119a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
74129a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return atLeastOneDigit;
74139a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
74149a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
74154a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /**
74167a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * Takes components of a name from the query parameters and returns a cursor with those
74177a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * components as well as all missing components.  There is no database activity involved
74187a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * in this so the call can be made on the UI thread.
74197a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     */
74207a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private Cursor completeName(Uri uri, String[] projection) {
74217a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        if (projection == null) {
74227a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            projection = sDataProjectionMap.getColumnNames();
74237a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
74247a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
74257a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        ContentValues values = new ContentValues();
7426f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        DataRowHandlerForStructuredName handler = (DataRowHandlerForStructuredName)
7427f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                getDataRowHandler(StructuredName.CONTENT_ITEM_TYPE);
74287a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
74297a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        copyQueryParamsToContentValues(values, uri,
74307a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.DISPLAY_NAME,
74317a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PREFIX,
74327a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.GIVEN_NAME,
74337a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.MIDDLE_NAME,
74347a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.FAMILY_NAME,
74357a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.SUFFIX,
74367a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_NAME,
74377a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_FAMILY_NAME,
74387a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_MIDDLE_NAME,
74397a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_GIVEN_NAME
74407a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        );
74417a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
74427a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        handler.fixStructuredNameComponents(values, values);
74437a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
74447a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
74457a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        Object[] row = new Object[projection.length];
74467a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
74477a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            row[i] = values.get(projection[i]);
74487a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
74497a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        cursor.addRow(row);
74507a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        return cursor;
74517a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
74527a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
74537a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private void copyQueryParamsToContentValues(ContentValues values, Uri uri, String... columns) {
74547a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (String column : columns) {
74557a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            String param = uri.getQueryParameter(column);
74567a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            if (param != null) {
74577a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                values.put(column, param);
74587a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
74597a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
74607a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
74617a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
74627a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
74637a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    /**
74644a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     * Inserts an argument at the beginning of the selection arg list.
74654a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     */
74664a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private String[] insertSelectionArg(String[] selectionArgs, String arg) {
7467b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
7468b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
7469b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
7470b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
7471b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
74724a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            newSelectionArgs[0] = arg;
74734a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length);
7474b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
7475b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
7476b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
7477caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
74785e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private String[] appendProjectionArg(String[] projection, String arg) {
74795e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        if (projection == null) {
74805e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            return null;
74815e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        }
74825e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        final int length = projection.length;
74835e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        String[] newProjection = new String[length + 1];
74845e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        System.arraycopy(projection, 0, newProjection, 0, length);
74855e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        newProjection[length] = arg;
74865e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        return newProjection;
74875e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    }
74885e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
7489caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    protected Account getDefaultAccount() {
7490caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        AccountManager accountManager = AccountManager.get(getContext());
7491caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        try {
74925f1f4a062ac34d75d2dbf586702cbeb121cf09caDmitri Plotnikov            Account[] accounts = accountManager.getAccountsByType(DEFAULT_ACCOUNT_TYPE);
7493caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            if (accounts != null && accounts.length > 0) {
7494caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov                return accounts[0];
7495caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            }
7496caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        } catch (Throwable e) {
74976f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov            Log.e(TAG, "Cannot determine the default account for contacts compatibility", e);
7498caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        }
74996f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov        return null;
7500caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    }
7501f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
750273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    /**
750343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * Returns true if the specified account type and data set is writable.
750473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     */
750543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    protected boolean isWritableAccountWithDataSet(String accountTypeAndDataSet) {
750643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        if (accountTypeAndDataSet == null) {
7507bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov            return true;
7508bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        }
7509bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov
751043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        Boolean writable = mAccountWritability.get(accountTypeAndDataSet);
751173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable != null) {
751273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            return writable;
751373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
751473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
7515627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        IContentService contentService = ContentResolver.getContentService();
7516627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
751743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // TODO(dsantoro): Need to update this logic to allow for sub-accounts.
7518627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (SyncAdapterType sync : contentService.getSyncAdapterTypes()) {
7519627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (ContactsContract.AUTHORITY.equals(sync.authority) &&
752043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        accountTypeAndDataSet.equals(sync.accountType)) {
752173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    writable = sync.supportsUploading();
752273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    break;
7523627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
7524627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
7525627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } catch (RemoteException e) {
7526627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            Log.e(TAG, "Could not acquire sync adapter types");
7527627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
752873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
752973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable == null) {
753073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            writable = false;
753173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
753273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
753343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        mAccountWritability.put(accountTypeAndDataSet, writable);
753473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        return writable;
7535627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
7536b4e61e064b59e8076df81b061add9fb358fd2ed9Dmitri Plotnikov
7537d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
7538f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static boolean readBooleanQueryParameter(Uri uri, String parameter,
7539f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean defaultValue) {
7540f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7541f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        // Manually parse the query, which is much faster than calling uri.getQueryParameter
7542f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
7543f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
7544f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
7545f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7546f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7547f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = query.indexOf(parameter);
7548f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (index == -1) {
7549f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
7550f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7551f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7552f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        index += parameter.length();
7553f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7554f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return !matchQueryParameter(query, index, "=0", false)
7555f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && !matchQueryParameter(query, index, "=false", true);
7556f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
7557f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7558f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private static boolean matchQueryParameter(String query, int index, String value,
7559f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean ignoreCase) {
7560f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int length = value.length();
7561f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return query.regionMatches(ignoreCase, index, value, 0, length)
7562f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && (query.length() == index + length || query.charAt(index + length) == '&');
7563f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
7564f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7565f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /**
7566f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * A fast re-implementation of {@link Uri#getQueryParameter}
7567f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     */
7568f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static String getQueryParameter(Uri uri, String parameter) {
7569f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
7570f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
7571f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return null;
7572f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7573f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7574f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int queryLength = query.length();
7575f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int parameterLength = parameter.length();
7576f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7577f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String value;
7578f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = 0;
7579f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        while (true) {
7580f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index = query.indexOf(parameter, index);
7581f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (index == -1) {
7582f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
75835fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            }
75845fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa
75855fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            // Should match against the whole parameter instead of its suffix.
75865fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            // e.g. The parameter "param" must not be found in "some_param=val".
75875fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            if (index > 0) {
75885fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                char prevChar = query.charAt(index - 1);
75895fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                if (prevChar != '?' && prevChar != '&') {
75905fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    // With "some_param=val1&param=val2", we should find second "param" occurrence.
75915fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    index += parameterLength;
75925fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    continue;
75935fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                }
7594f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
7595f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7596f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index += parameterLength;
7597f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7598f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (queryLength == index) {
7599f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
7600f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
7601f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7602f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (query.charAt(index) == '=') {
7603f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                index++;
7604f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                break;
7605f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
7606f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7607f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7608f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int ampIndex = query.indexOf('&', index);
7609f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (ampIndex == -1) {
7610f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index);
7611f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        } else {
7612f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index, ampIndex);
7613f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7614f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7615f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return Uri.decode(value);
7616f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
76175dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
76180dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    protected boolean isAggregationUpgradeNeeded() {
76190dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
76200dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            return false;
76210dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
76220dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
76235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int version = Integer.parseInt(mContactsHelper.getProperty(
76245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                PROPERTY_AGGREGATION_ALGORITHM, "1"));
76250dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        return version < PROPERTY_AGGREGATION_ALGORITHM_VERSION;
76260dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
76270dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
7628bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void upgradeAggregationAlgorithmInBackground() {
76290dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // This upgrade will affect very few contacts, so it can be performed on the
76300dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // main thread during the initial boot after an OTA
76310dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
76320dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        Log.i(TAG, "Upgrading aggregation algorithm");
76330dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int count = 0;
76340dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        long start = SystemClock.currentThreadTimeMillis();
76355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        SQLiteDatabase db = null;
76360dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        try {
76375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToContactMode();
76385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db = mContactsHelper.getWritableDatabase();
76395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.set(db);
76405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db.beginTransaction();
76415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            Cursor cursor = db.query(true,
76420dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    Tables.RAW_CONTACTS + " r1 JOIN " + Tables.RAW_CONTACTS + " r2",
76430dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    new String[]{"r1." + RawContacts._ID},
76440dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    "r1." + RawContacts._ID + "!=r2." + RawContacts._ID +
76450dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.CONTACT_ID + "=r2." + RawContacts.CONTACT_ID +
76460dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.ACCOUNT_NAME + "=r2." + RawContacts.ACCOUNT_NAME +
764743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    " AND r1." + RawContacts.ACCOUNT_TYPE + "=r2." + RawContacts.ACCOUNT_TYPE +
764843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    " AND r1." + RawContacts.DATA_SET + "=r2." + RawContacts.DATA_SET,
76490dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    null, null, null, null, null);
76500dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            try {
76510dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                while (cursor.moveToNext()) {
76520dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    long rawContactId = cursor.getLong(0);
76530dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId,
76540dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                            RawContacts.AGGREGATION_MODE_DEFAULT, true);
76550dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    count++;
76560dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                }
76570dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            } finally {
76580dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                cursor.close();
76590dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            }
76605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mContactAggregator.aggregateInTransaction(mTransactionContext.get(), db);
7661bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            updateSearchIndexInTransaction();
76625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db.setTransactionSuccessful();
76635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mContactsHelper.setProperty(PROPERTY_AGGREGATION_ALGORITHM,
76640dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    String.valueOf(PROPERTY_AGGREGATION_ALGORITHM_VERSION));
76650dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        } finally {
76665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (db != null) {
76675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                db.endTransaction();
76685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
76690dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            long end = SystemClock.currentThreadTimeMillis();
76700dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            Log.i(TAG, "Aggregation algorithm upgraded for " + count
76710dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    + " contacts, in " + (end - start) + "ms");
76720dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
76730dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
76749a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
76759a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    /* Visible for testing */
76769a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    boolean isPhone() {
76779a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (!sIsPhoneInitialized) {
76789a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sIsPhone = new TelephonyManager(getContext()).isVoiceCapable();
76799a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sIsPhoneInitialized = true;
76809a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
76819a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return sIsPhone;
76829a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
768346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
768446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private boolean handleDataUsageFeedback(Uri uri) {
768546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final long currentTimeMillis = System.currentTimeMillis();
768646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String usageType = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
768746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] ids = uri.getLastPathSegment().trim().split(",");
768846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final ArrayList<Long> dataIds = new ArrayList<Long>();
768946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
769046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        for (String id : ids) {
769146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            dataIds.add(Long.valueOf(id));
769246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
769346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final boolean successful;
769446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (TextUtils.isEmpty(usageType)) {
769546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            Log.w(TAG, "Method for data usage feedback isn't specified. Ignoring.");
769646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            successful = false;
769746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } else {
769846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            successful = updateDataUsageStat(dataIds, usageType, currentTimeMillis) > 0;
769946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
770046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
770146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        // Handle old API. This doesn't affect the result of this entire method.
770246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] questionMarks = new String[ids.length];
770346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        Arrays.fill(questionMarks, "?");
770446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String where = Data._ID + " IN (" + TextUtils.join(",", questionMarks) + ")";
77055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        final Cursor cursor = mActiveDb.get().query(
7706ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                Views.DATA,
770746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                new String[] { Data.CONTACT_ID },
770846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                where, ids, null, null, null);
770946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        try {
771046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            while (cursor.moveToNext()) {
771146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mSelectionArgs1[0] = cursor.getString(0);
771246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                ContentValues values2 = new ContentValues();
771346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                values2.put(Contacts.LAST_TIME_CONTACTED, currentTimeMillis);
77145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mActiveDb.get().update(Tables.CONTACTS, values2, Contacts._ID + "=?",
77155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mSelectionArgs1);
77165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mActiveDb.get().execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
77175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mActiveDb.get().execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
771846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
771946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } finally {
772046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            cursor.close();
772146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
772246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
772346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        return successful;
772446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
772546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
772646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
772746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Update {@link Tables#DATA_USAGE_STAT}.
772846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     *
772946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * @return the number of rows affected.
773046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
7731f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa    @VisibleForTesting
7732f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa    /* package */ int updateDataUsageStat(
7733f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa            List<Long> dataIds, String type, long currentTimeMillis) {
773446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final int typeInt = sDataUsageTypeMap.get(type);
773546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String where = DataUsageStatColumns.DATA_ID + " =? AND "
773646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                + DataUsageStatColumns.USAGE_TYPE_INT + " =?";
773746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] columns =
773846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                new String[] { DataUsageStatColumns._ID, DataUsageStatColumns.TIMES_USED };
773946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final ContentValues values = new ContentValues();
774046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        for (Long dataId : dataIds) {
774146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            final String[] args = new String[] { dataId.toString(), String.valueOf(typeInt) };
77425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().beginTransaction();
774346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            try {
77445d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                final Cursor cursor = mActiveDb.get().query(Tables.DATA_USAGE_STAT, columns, where,
77455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        args, null, null, null);
774646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                try {
774746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (cursor.getCount() > 0) {
774846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        if (!cursor.moveToFirst()) {
774946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            Log.e(TAG,
775046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    "moveToFirst() failed while getAccount() returned non-zero.");
775146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        } else {
775246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.clear();
775346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.put(DataUsageStatColumns.TIMES_USED, cursor.getInt(1) + 1);
775446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.put(DataUsageStatColumns.LAST_TIME_USED, currentTimeMillis);
77555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            mActiveDb.get().update(Tables.DATA_USAGE_STAT, values,
775646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    DataUsageStatColumns._ID + " =?",
775746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    new String[] { cursor.getString(0) });
775846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        }
775946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    } else {
776046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.clear();
776146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.DATA_ID, dataId);
776246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.USAGE_TYPE_INT, typeInt);
776346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.TIMES_USED, 1);
776446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.LAST_TIME_USED, currentTimeMillis);
77655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mActiveDb.get().insert(Tables.DATA_USAGE_STAT, null, values);
776646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    }
77675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mActiveDb.get().setTransactionSuccessful();
776846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                } finally {
776946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    cursor.close();
777046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
777146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            } finally {
77725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mActiveDb.get().endTransaction();
777346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
777446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
777546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
777646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        return dataIds.size();
777746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
777846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
777946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
778046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Returns a sort order String for promoting data rows (email addresses, phone numbers, etc.)
778146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * associated with a primary account. The primary account should be supplied from applications
778246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * with {@link ContactsContract#PRIMARY_ACCOUNT_NAME} and
778346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * {@link ContactsContract#PRIMARY_ACCOUNT_TYPE}. Null will be returned when the primary
778446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * account isn't available.
778546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
778646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private String getAccountPromotionSortOrder(Uri uri) {
778746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String primaryAccountName =
778846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME);
778946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String primaryAccountType =
779046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE);
779146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
779246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        // Data rows associated with primary account should be promoted.
779346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (!TextUtils.isEmpty(primaryAccountName)) {
779446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            StringBuilder sb = new StringBuilder();
779546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            sb.append("(CASE WHEN " + RawContacts.ACCOUNT_NAME + "=");
779646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            DatabaseUtils.appendEscapedSQLString(sb, primaryAccountName);
779746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            if (!TextUtils.isEmpty(primaryAccountType)) {
779846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                sb.append(" AND " + RawContacts.ACCOUNT_TYPE + "=");
779946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DatabaseUtils.appendEscapedSQLString(sb, primaryAccountType);
780046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
780146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            sb.append(" THEN 0 ELSE 1 END)");
780246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            return sb.toString();
780346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } else {
780446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            return null;
780546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
780646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
7807b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
7808b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    /**
7809b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * Checks the URI for a deferred snippeting request
7810b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * @return a boolean indicating if a deferred snippeting request is in the RI
7811b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     */
7812b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private boolean deferredSnippetingRequested(Uri uri) {
7813b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        String deferredSnippeting =
7814b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            getQueryParameter(uri, SearchSnippetColumns.DEFERRED_SNIPPETING_KEY);
7815b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        return !TextUtils.isEmpty(deferredSnippeting) &&  deferredSnippeting.equals("1");
7816b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    }
7817b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
7818b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    /**
7819b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * Checks if query is a single word or not.
7820b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * @return a boolean indicating if the query is one word or not
7821b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     */
7822b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private boolean isSingleWordQuery(String query) {
7823b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        return query.split(QUERY_TOKENIZER_REGEX).length == 1;
7824b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    }
7825b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
7826b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    /**
7827b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * Checks the projection for a SNIPPET column indicating that a snippet is needed
7828b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * @return a boolean indicating if a snippet is needed or not.
7829b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     */
7830b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private boolean snippetNeeded(String [] projection) {
7831b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        return mDbHelper.get().isInProjection(projection, SearchSnippetColumns.SNIPPET);
7832b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    }
78334f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
7834