ContactsProvider2.java revision bd20dbedba706fdf2db7acb1c7d4391e57129d44
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;
139916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikovimport android.provider.ContactsContract.SearchSnippetColumns;
1403de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Settings;
14182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikovimport android.provider.ContactsContract.StatusUpdates;
1423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmannimport android.provider.ContactsContract.StreamItemPhotos;
143f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.provider.ContactsContract.StreamItems;
14497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.LiveFolders;
14597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.OpenableColumns;
14697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.SyncStateContract;
147a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.telephony.PhoneNumberUtils;
1489a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikovimport android.telephony.TelephonyManager;
149a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.text.TextUtils;
150c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.util.Log;
1514f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
152108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawaimport java.io.BufferedWriter;
153d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.ByteArrayOutputStream;
154f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport java.io.File;
155b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport java.io.FileNotFoundException;
156d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.IOException;
157d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.OutputStream;
158108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawaimport java.io.OutputStreamWriter;
159108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawaimport java.io.Writer;
16042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.text.SimpleDateFormat;
1617e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport java.util.ArrayList;
16246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawaimport java.util.Arrays;
1635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.Collections;
16442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.util.Date;
165b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport java.util.HashMap;
1660e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.HashSet;
1675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.List;
168622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkeyimport java.util.Locale;
169b5a4add17815167d20a90645779df34cdf45280dFred Quintanaimport java.util.Map;
1700e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.Set;
171ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikovimport java.util.concurrent.CountDownLatch;
1724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/**
1744f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Contacts content provider. The contract between this provider and applications
1754f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * is defined in {@link ContactsContract}.
1764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
1775b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanapublic class ContactsProvider2 extends SQLiteContentProvider implements OnAccountsUpdateListener {
178caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
179bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final String TAG = "ContactsProvider";
180bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov
181bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE);
1824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
18315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_INITIALIZE = 0;
18415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_OPEN_WRITE_ACCESS = 1;
18515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS = 2;
18615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_ACCOUNTS = 3;
18715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_LOCALE = 4;
18815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM = 5;
18905e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_SEARCH_INDEX = 6;
19005e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_PROVIDER_STATUS = 7;
19105e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_DIRECTORIES = 8;
19205e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_CHANGE_LOCALE = 9;
193f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int BACKGROUND_TASK_CLEANUP_PHOTOS = 10;
194619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Default for the maximum number of returned aggregation suggestions. */
1963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DEFAULT_MAX_SUGGESTIONS = 5;
1973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /** Limit for the maximum number of social stream items to store under a raw contact. */
1993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int MAX_STREAM_ITEMS_PER_RAW_CONTACT = 5;
2003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
201f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /** Rate limit (in ms) for photo cleanup.  Do it at most once per day. */
202f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int PHOTO_CLEANUP_RATE_LIMIT = 24 * 60 * 60 * 1000;
203f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
2043d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /**
205b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov     * Property key for the legacy contact import version. The need for a version
2063d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * as opposed to a boolean flag is that if we discover bugs in the contact import process,
2073d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * we can trigger re-import by incrementing the import version.
2083d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     */
209b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final String PROPERTY_CONTACTS_IMPORTED = "contacts_imported_v1";
210b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final int PROPERTY_CONTACTS_IMPORT_VERSION = 1;
21151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    private static final String PREF_LOCALE = "locale";
2123d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2130dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static final String PROPERTY_AGGREGATION_ALGORITHM = "aggregation_v2";
2140dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static final int PROPERTY_AGGREGATION_ALGORITHM_VERSION = 2;
2150dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
2160e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff    private static final String AGGREGATE_CONTACTS = "sync.contacts.aggregate";
2170e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff
2185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private static final ProfileAwareUriMatcher sUriMatcher =
2195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            new ProfileAwareUriMatcher(UriMatcher.NO_MATCH);
2204f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2212f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    /**
2222f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     * Used to insert a column into strequent results, which enables SQL to sort the list using
2232f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     * the total times contacted. See also {@link #sStrequentFrequentProjectionMap}.
2242f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     */
2252f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    private static final String TIMES_USED_SORT_COLUMN = "times_used_sort";
2265e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
227d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final String STREQUENT_ORDER_BY = Contacts.STARRED + " DESC, "
2282f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            + TIMES_USED_SORT_COLUMN + " DESC, "
2299b43551f1ce33b79141772737a262ce609bd0cebMegha Joshi            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
230d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final String STREQUENT_LIMIT =
231d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            "(SELECT COUNT(1) FROM " + Tables.CONTACTS + " WHERE "
232d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            + Contacts.STARRED + "=1) + 25";
233d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
23445ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa    private static final String FREQUENT_ORDER_BY = DataUsageStatColumns.TIMES_USED + " DESC,"
23545ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
23645ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa
2376e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_CONTACTS_TABLE =
2389b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.CONTACTS + " SET " + Contacts.TIMES_CONTACTED + "=" +
2399b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + Contacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
2409b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + Contacts.TIMES_CONTACTED + " + 1) END WHERE " + Contacts._ID + "=?";
2419b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
2426e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE =
2439b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.RAW_CONTACTS + " SET " + RawContacts.TIMES_CONTACTED + "=" +
2449b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + RawContacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
2459b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + RawContacts.TIMES_CONTACTED + " + 1) END WHERE " + RawContacts.CONTACT_ID + "=?";
2469b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
247de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa    /* package */ static final String PHONEBOOK_COLLATOR_NAME = "PHONEBOOK";
248de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa
2493716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    // Regex for splitting query strings - we split on any group of non-alphanumeric characters,
2503716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    // excluding the @ symbol.
2513716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    /* package */ static final String QUERY_TOKENIZER_REGEX = "[^\\w@]+";
2523716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
253d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS = 1000;
254d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS_ID = 1001;
2555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP = 1002;
2565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID = 1003;
257a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_DATA = 1004;
2585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_FILTER = 1005;
2595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT = 1006;
2605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT_FILTER = 1007;
2615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_GROUP = 1008;
262a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_PHOTO = 1009;
263bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_PHOTO = 1010;
264bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_PHOTO = 1011;
265bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_ID_DISPLAY_PHOTO = 1012;
266bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_DISPLAY_PHOTO = 1013;
267bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_DISPLAY_PHOTO = 1014;
268bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_AS_VCARD = 1015;
269bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_AS_MULTI_VCARD = 1016;
270bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_DATA = 1017;
271bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_DATA = 1018;
272bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_ID_ENTITIES = 1019;
273bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ENTITIES = 1020;
274bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_ENTITIES = 1021;
275bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_ID_STREAM_ITEMS = 1022;
276bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_STREAM_ITEMS = 1023;
277bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_STREAM_ITEMS = 1024;
278bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_FREQUENT = 1025;
2794f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2805ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS = 2002;
2815ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_ID = 2003;
2825ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_DATA = 2004;
28346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITY_ID = 2005;
284f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int RAW_CONTACTS_ID_DISPLAY_PHOTO = 2006;
285f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int RAW_CONTACTS_ID_STREAM_ITEMS = 2007;
28682780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro    private static final int RAW_CONTACTS_ID_STREAM_ITEMS_ID = 2008;
2874f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2886bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
2896bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
290ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
29148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_ID = 3003;
29248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_FILTER = 3004;
29348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS = 3005;
29448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_ID = 3006;
29548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_LOOKUP = 3007;
29648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_FILTER = 3008;
29748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS = 3009;
29848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS_ID = 3010;
299a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
3006bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
3016bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
302b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
303b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
304b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
30582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES = 7000;
30682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES_ID = 7001;
3071f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
30831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
30931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
310eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final int SETTINGS = 9000;
311eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
312ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
313ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
314ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
315ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
31635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
317b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private static final int SYNCSTATE_ID = 11001;
3185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private static final int PROFILE_SYNCSTATE = 11002;
3195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private static final int PROFILE_SYNCSTATE_ID = 11003;
32035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
321c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SUGGESTIONS = 12001;
322c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SHORTCUT = 12002;
323c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
3241b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS = 14000;
3251b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_WITH_PHONES = 14001;
3261b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_FAVORITES = 14002;
3271b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_GROUP_NAME = 14003;
3281b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
32946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITIES = 15001;
33046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
33109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private static final int PROVIDER_STATUS = 16001;
33209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
333d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES = 17001;
334d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES_ID = 17002;
335d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3367a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private static final int COMPLETE_NAME = 18000;
3377a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
33824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE = 19000;
33924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_ENTITIES = 19001;
34024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_DATA = 19002;
34124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_DATA_ID = 19003;
34224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_AS_VCARD = 19004;
34324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS = 19005;
34424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID = 19006;
34524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID_DATA = 19007;
34624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID_ENTITIES = 19008;
3475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private static final int PROFILE_STATUS_UPDATES = 19009;
3483202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro    private static final int PROFILE_RAW_CONTACT_ENTITIES = 19010;
34924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
35046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final int DATA_USAGE_FEEDBACK_ID = 20001;
35146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
3523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS = 21000;
3533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_PHOTOS = 21001;
3543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID = 21002;
3553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID_PHOTOS = 21003;
3563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID_PHOTOS_ID = 21004;
3573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_LIMIT = 21005;
3583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
359f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int DISPLAY_PHOTO = 22000;
360f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int PHOTO_DIMENSIONS = 22001;
361f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
3625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Inserts into URIs in this map will direct to the profile database if the parent record's
3635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // value (looked up from the ContentValues object with the key specified by the value in this
3645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // map) is in the profile ID-space (see {@link ProfileDatabaseHelper#PROFILE_ID_SPACE}).
3655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private static final Map<Integer, String> INSERT_URI_ID_VALUE_MAP = Maps.newHashMap();
3665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    static {
3675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(DATA, Data.RAW_CONTACT_ID);
3685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(RAW_CONTACTS_DATA, Data.RAW_CONTACT_ID);
3695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(STATUS_UPDATES, StatusUpdates.DATA_ID);
3705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(STREAM_ITEMS, StreamItems.RAW_CONTACT_ID);
3715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(RAW_CONTACTS_ID_STREAM_ITEMS, StreamItems.RAW_CONTACT_ID);
3725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(STREAM_ITEMS_PHOTOS, StreamItemPhotos.STREAM_ITEM_ID);
3735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(STREAM_ITEMS_ID_PHOTOS, StreamItemPhotos.STREAM_ITEM_ID);
3745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
3755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
376dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID =
377dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
378dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME
379dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
380dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE
38143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AND ("
38243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + GroupsColumns.CONCRETE_DATA_SET
38343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + "=" + RawContactsColumns.CONCRETE_DATA_SET + " OR "
38443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + GroupsColumns.CONCRETE_DATA_SET + " IS NULL AND "
38543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + RawContactsColumns.CONCRETE_DATA_SET + " IS NULL)"
386dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " AND " + Groups.FAVORITES + " != 0";
387dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
388dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID =
389dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
390dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
391dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
392dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
39343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AND ("
39443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + GroupsColumns.CONCRETE_DATA_SET + "="
39543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + RawContactsColumns.CONCRETE_DATA_SET + " OR "
39643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + GroupsColumns.CONCRETE_DATA_SET + " IS NULL AND "
39743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + RawContactsColumns.CONCRETE_DATA_SET + " IS NULL)"
39843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + " AND " + Groups.AUTO_ADD + " != 0";
399dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
400dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String[] PROJECTION_GROUP_ID
401dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            = new String[]{Tables.GROUPS + "." + Groups._ID};
402dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
403dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_GROUPMEMBERSHIP_DATA = DataColumns.MIMETYPE_ID + "=? "
404dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.GROUP_ROW_ID + "=? "
405dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.RAW_CONTACT_ID + "=?";
406dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
407dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_STARRED_FROM_RAW_CONTACTS =
408dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            "SELECT " + RawContacts.STARRED
409dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " FROM " + Tables.RAW_CONTACTS + " WHERE " + RawContacts._ID + "=?";
410dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
411d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private interface DataContactsQuery {
412f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        public static final String TABLE = "data "
413f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) "
414f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN contacts ON (raw_contacts.contact_id = contacts._id)";
41567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
41667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
4176cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
4186802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            RawContactsColumns.CONCRETE_ACCOUNT_TYPE,
4196802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            RawContactsColumns.CONCRETE_ACCOUNT_NAME,
42043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            RawContactsColumns.CONCRETE_DATA_SET,
4213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
422f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            ContactsColumns.CONCRETE_ID
423ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
424ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
425d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
4266802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        public static final int ACCOUNT_TYPE = 1;
4276802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        public static final int ACCOUNT_NAME = 2;
42843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        public static final int DATA_SET = 3;
42943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        public static final int DATA_ID = 4;
43043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        public static final int CONTACT_ID = 5;
431ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
4321f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
433f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov    interface RawContactsQuery {
43419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String TABLE = Tables.RAW_CONTACTS;
43519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
43619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String[] COLUMNS = new String[] {
437ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.DELETED,
438ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
439ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
44043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.DATA_SET,
44119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        };
44219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
44319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int DELETED = 0;
444ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_TYPE = 1;
445ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_NAME = 2;
44643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int DATA_SET = 3;
44719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    }
44819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
449c76cdd0723b99f478c9ba5329d14a971cd8dfb3dCostin Manolache    public static final String DEFAULT_ACCOUNT_TYPE = "com.google";
450caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
45171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    /** Sql where statement for filtering on groups. */
45271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private static final String CONTACTS_IN_GROUP_SELECT =
45371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov            Contacts._ID + " IN "
45471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + "(SELECT " + RawContacts.CONTACT_ID
45571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " FROM " + Tables.RAW_CONTACTS
45671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + " IN "
45771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
45871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " FROM " + Tables.DATA_JOIN_MIMETYPES
4597cf50494501938f175d288077145acf49da8f171Daniel Lehmann                            + " WHERE " + DataColumns.MIMETYPE_ID + "=?"
4607cf50494501938f175d288077145acf49da8f171Daniel Lehmann                                    + " AND " + GroupMembership.GROUP_ROW_ID + "="
46171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "(SELECT " + Tables.GROUPS + "." + Groups._ID
46271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " FROM " + Tables.GROUPS
46371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " WHERE " + Groups.TITLE + "=?)))";
46471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov
465a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating DIRTY flag on multiple raw contacts */
466a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_DIRTY_SQL =
467a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
468a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.DIRTY + "=1" +
469a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
470a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
471a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating VERSION on multiple raw contacts */
472a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_VERSION_SQL =
473a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
474a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.VERSION + " = " + RawContacts.VERSION + " + 1" +
475a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
476a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
477c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Current contacts - those contacted within the last 3 days (in seconds)
478c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_CURRENT = 3 * 24 * 60 * 60;
479c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
480c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Recent contacts - those contacted within the last 30 days (in seconds)
481c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_RECENT = 30 * 24 * 60 * 60;
482c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
483f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa    private static final String TIME_SINCE_LAST_USED =
484f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa            "(strftime('%s', 'now') - " + DataUsageStatColumns.LAST_TIME_USED + "/1000)";
485f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa
486c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    /*
487c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * Sorting order for email address suggestions: first starred, then the rest.
4882262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * second in_visible_group, then the rest.
4892262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * Within the four (starred/unstarred, in_visible_group/not-in_visible_group) groups
4902262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * - three buckets: very recently contacted, then fairly
491c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * recently contacted, then the rest.  Within each of the bucket - descending count
49246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * of times contacted (both for data row and for contact row). If all else fails, alphabetical.
49346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * (Super)primary email address is returned before other addresses for the same contact.
494c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     */
495c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final String EMAIL_FILTER_SORT_ORDER =
4962262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa        Contacts.STARRED + " DESC, "
4972262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa        + Contacts.IN_VISIBLE_GROUP + " DESC, "
498f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa        + "(CASE WHEN " + TIME_SINCE_LAST_USED + " < " + EMAIL_FILTER_CURRENT
49946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " THEN 0 "
500f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa                + " WHEN " + TIME_SINCE_LAST_USED + " < " + EMAIL_FILTER_RECENT
50146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " THEN 1 "
50246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " ELSE 2 END), "
50346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + DataUsageStatColumns.TIMES_USED + " DESC, "
50446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + Contacts.DISPLAY_NAME + ", "
50546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + Data.CONTACT_ID + ", "
506c591cc2ffecdd0038f787a133606752752294c13Daisuke Miyakawa        + Data.IS_SUPER_PRIMARY + " DESC, "
507c591cc2ffecdd0038f787a133606752752294c13Daisuke Miyakawa        + Data.IS_PRIMARY + " DESC";
50846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
50946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /** Currently same as {@link #EMAIL_FILTER_SORT_ORDER} */
51046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final String PHONE_FILTER_SORT_ORDER = EMAIL_FILTER_SORT_ORDER;
511c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
512916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Name lookup types used for contact filtering */
513916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private static final String CONTACT_LOOKUP_NAME_TYPES =
514916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NAME_COLLATION_KEY + "," +
515916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.EMAIL_BASED_NICKNAME + "," +
51692ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov            NameLookupType.NICKNAME;
517916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
518f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    /**
519f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * If any of these columns are used in a Data projection, there is no point in
520f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * using the DISTINCT keyword, which can negatively affect performance.
521f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     */
522f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    private static final String[] DISTINCT_DATA_PROHIBITING_COLUMNS = {
523f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data._ID,
524f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.RAW_CONTACT_ID,
525f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.NAME_RAW_CONTACT_ID,
526f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_NAME,
527f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_TYPE,
52843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            RawContacts.DATA_SET,
52943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
530f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.DIRTY,
531f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.NAME_VERIFIED,
532f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.SOURCE_ID,
533f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.VERSION,
534f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    };
535916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
536f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsColumns = ProjectionMap.builder()
537f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CUSTOM_RINGTONE)
538f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME)
539f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_ALTERNATIVE)
540f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_SOURCE)
541f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.IN_VISIBLE_GROUP)
542f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LAST_TIME_CONTACTED)
543f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LOOKUP_KEY)
544f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME)
545f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME_STYLE)
546f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHOTO_ID)
547f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            .add(Contacts.PHOTO_FILE_ID)
5483d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_URI)
5493d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_THUMBNAIL_URI)
550f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SEND_TO_VOICEMAIL)
551f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_ALTERNATIVE)
552f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_PRIMARY)
553f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.STARRED)
554f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.TIMES_CONTACTED)
555cf832869bcf91b8037d8b7f510a3a213b30764a3Dmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
556f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
557f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
558f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsPresenceColumns = ProjectionMap.builder()
559f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
560f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE)
561f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
562f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
563f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
564f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
565f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
566f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
567f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
568f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
569f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
570f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
571f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
572f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
573f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
574f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
575f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSnippetColumns = ProjectionMap.builder()
57603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET)
577f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
578f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
579f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactColumns = ProjectionMap.builder()
580f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_NAME)
581f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_TYPE)
58243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(RawContacts.DATA_SET)
58343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(RawContacts.ACCOUNT_TYPE_AND_DATA_SET)
584f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DIRTY)
585f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.NAME_VERIFIED)
586f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SOURCE_ID)
587f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.VERSION)
588f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
589f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
590f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactSyncColumns = ProjectionMap.builder()
591f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC1)
592f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC2)
593f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC3)
594f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC4)
595f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
596f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
597f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataColumns = ProjectionMap.builder()
598f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA1)
599f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA2)
600f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA3)
601f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA4)
602f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA5)
603f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA6)
604f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA7)
605f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA8)
606f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA9)
607f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA10)
608f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA11)
609f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA12)
610f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA13)
611f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA14)
612f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA15)
613f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA_VERSION)
614f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_PRIMARY)
615f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_SUPER_PRIMARY)
616f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.MIMETYPE)
617f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RES_PACKAGE)
618f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC1)
619f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC2)
620f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC3)
621f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC4)
622f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(GroupMembership.GROUP_SOURCE_ID)
623f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
624f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
625f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactPresenceColumns = ProjectionMap.builder()
626f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
627f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.PRESENCE)
628f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
629f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.CHAT_CAPABILITY)
630f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
631f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
632f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
633f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
634f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
635f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
636f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
637f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
638f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
639f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
640f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
641f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
642f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataPresenceColumns = ProjectionMap.builder()
643f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.PRESENCE, Tables.PRESENCE + "." + StatusUpdates.PRESENCE)
644f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CHAT_CAPABILITY, Tables.PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
645f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS, StatusUpdatesColumns.CONCRETE_STATUS)
646f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_TIMESTAMP, StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
647f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_RES_PACKAGE, StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
648f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_LABEL, StatusUpdatesColumns.CONCRETE_STATUS_LABEL)
649f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_ICON, StatusUpdatesColumns.CONCRETE_STATUS_ICON)
650f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
651f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
652038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    /** Contains just BaseColumns._COUNT */
653f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sCountProjectionMap = ProjectionMap.builder()
654f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(BaseColumns._COUNT, "COUNT(*)")
655f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
656f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
657e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    /** Contains just the contacts columns */
658f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionMap = ProjectionMap.builder()
659f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts._ID)
660f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
661f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.NAME_RAW_CONTACT_ID)
66224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(Contacts.IS_USER_PROFILE)
663f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
664f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsPresenceColumns)
665f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
666f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
667916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Contains just the contacts columns */
668f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionWithSnippetMap = ProjectionMap.builder()
669f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
670f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sSnippetColumns)
671f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
672916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
6735e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    /** Used for pushing starred contacts to the top of a times contacted list **/
674f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentStarredProjectionMap = ProjectionMap.builder()
675f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
6762f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, String.valueOf(Long.MAX_VALUE))
677f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
678f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
679f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentFrequentProjectionMap = ProjectionMap.builder()
680f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
6812f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, "SUM(" + DataUsageStatColumns.CONCRETE_TIMES_USED + ")")
682f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
683f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6844928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    /**
6854928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * Used for Strequent Uri with {@link ContactsContract#STREQUENT_PHONE_ONLY}, which allows
6864928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * users to obtain part of Data columns. Right now Starred part just returns NULL for
6874928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * those data columns (frequent part should return real ones in data table).
6884928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     **/
6894928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    private static final ProjectionMap sStrequentPhoneOnlyStarredProjectionMap
6904928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            = ProjectionMap.builder()
6914928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .addAll(sContactsProjectionMap)
6924928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, String.valueOf(Long.MAX_VALUE))
6934928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.NUMBER, "NULL")
6944928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.TYPE, "NULL")
6954928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.LABEL, "NULL")
6964928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .build();
6974928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
6984928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    /**
6994928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * Used for Strequent Uri with {@link ContactsContract#STREQUENT_PHONE_ONLY}, which allows
7004928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * users to obtain part of Data columns. We hard-code {@link Contacts#IS_USER_PROFILE} to NULL,
7014928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * because sContactsProjectionMap specifies a field that doesn't exist in the view behind the
7024928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * query that uses this projection map.
7034928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     **/
7044928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    private static final ProjectionMap sStrequentPhoneOnlyFrequentProjectionMap
7054928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            = ProjectionMap.builder()
7064928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .addAll(sContactsProjectionMap)
7074928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, DataUsageStatColumns.CONCRETE_TIMES_USED)
7084928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.NUMBER)
7094928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.TYPE)
7104928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.LABEL)
7114928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Contacts.IS_USER_PROFILE, "NULL")
7124928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .build();
7134928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
714f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    /** Contains just the contacts vCard columns */
715f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsVCardProjectionMap = ProjectionMap.builder()
716fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            .add(Contacts._ID)
717f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.DISPLAY_NAME, Contacts.DISPLAY_NAME + " || '.vcf'")
718f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.SIZE, "NULL")
719f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
720f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
721ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    /** Contains just the raw contacts columns */
722f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactsProjectionMap = ProjectionMap.builder()
723f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
724f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
725f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
726f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_PRIMARY)
727f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_ALTERNATIVE)
728f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_SOURCE)
729f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME)
730f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME_STYLE)
731f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_PRIMARY)
732f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_ALTERNATIVE)
733f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.TIMES_CONTACTED)
734f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.LAST_TIME_CONTACTED)
735f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CUSTOM_RINGTONE)
736f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SEND_TO_VOICEMAIL)
737f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
738f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.AGGREGATION_MODE)
73924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
740f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
741f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
742f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
743f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
744a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the raw entity view*/
745f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawEntityProjectionMap = ProjectionMap.builder()
746f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
747f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
748f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.Entity.DATA_ID)
749f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
750f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
75124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
752f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
753f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
754f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
755f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
756f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
757a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the contact entity view*/
758f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sEntityProjectionMap = ProjectionMap.builder()
759f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity._ID)
760f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.CONTACT_ID)
761f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.RAW_CONTACT_ID)
762f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DATA_ID)
763f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.NAME_RAW_CONTACT_ID)
764f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DELETED)
76524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(Contacts.IS_USER_PROFILE)
766f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
767f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
768f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
769f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
770f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
771f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
772f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
773f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
7744a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /** Contains columns from the data view */
775f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataProjectionMap = ProjectionMap.builder()
776f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID)
777f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RAW_CONTACT_ID)
778f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CONTACT_ID)
779f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.NAME_RAW_CONTACT_ID)
78024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
781f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
782f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
783f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
784f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
785f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
786f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
787f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
7885e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /** Contains columns from the data view */
789f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDistinctDataProjectionMap = ProjectionMap.builder()
790f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID, "MIN(" + Data._ID + ")")
791f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
79224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
793f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
794f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
795f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
796f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
797f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
798f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
7999261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
800f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sPhoneLookupProjectionMap = ProjectionMap.builder()
801f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup._ID, "contacts_view." + Contacts._ID)
802f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LOOKUP_KEY, "contacts_view." + Contacts.LOOKUP_KEY)
803f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.DISPLAY_NAME, "contacts_view." + Contacts.DISPLAY_NAME)
804f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LAST_TIME_CONTACTED, "contacts_view." + Contacts.LAST_TIME_CONTACTED)
805f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TIMES_CONTACTED, "contacts_view." + Contacts.TIMES_CONTACTED)
806f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.STARRED, "contacts_view." + Contacts.STARRED)
807f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.IN_VISIBLE_GROUP, "contacts_view." + Contacts.IN_VISIBLE_GROUP)
808f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.PHOTO_ID, "contacts_view." + Contacts.PHOTO_ID)
8093d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_URI, "contacts_view." + Contacts.PHOTO_URI)
8103d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_THUMBNAIL_URI, "contacts_view." + Contacts.PHOTO_THUMBNAIL_URI)
811f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.CUSTOM_RINGTONE, "contacts_view." + Contacts.CUSTOM_RINGTONE)
812f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.HAS_PHONE_NUMBER, "contacts_view." + Contacts.HAS_PHONE_NUMBER)
813f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.SEND_TO_VOICEMAIL, "contacts_view." + Contacts.SEND_TO_VOICEMAIL)
814f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.NUMBER, Phone.NUMBER)
815f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TYPE, Phone.TYPE)
816f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LABEL, Phone.LABEL)
8172530512f639c4979fd7371c7dd25dd67e8118124Bai Tao            .add(PhoneLookup.NORMALIZED_NUMBER, Phone.NORMALIZED_NUMBER)
818f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
819f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
820ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
821f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsProjectionMap = ProjectionMap.builder()
822f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups._ID)
823f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_NAME)
824f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_TYPE)
82543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(Groups.DATA_SET)
82643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(Groups.ACCOUNT_TYPE_AND_DATA_SET)
827f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SOURCE_ID)
828f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DIRTY)
829f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.VERSION)
830f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.RES_PACKAGE)
831f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE)
832f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE_RES)
833f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.GROUP_VISIBLE)
834f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYSTEM_ID)
835f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DELETED)
836f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.NOTES)
837f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SHOULD_SYNC)
838f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.FAVORITES)
839f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.AUTO_ADD)
840c039cfb78c40730483fd71178df63ada5826a315Dmitri Plotnikov            .add(Groups.GROUP_IS_READ_ONLY)
841f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC1)
842f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC2)
843f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC3)
844f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC4)
845f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
846f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
847ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains {@link Groups} columns along with summary details */
848f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsSummaryProjectionMap = ProjectionMap.builder()
849f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sGroupsProjectionMap)
850f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_COUNT,
851f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                    "(SELECT COUNT(" + ContactsColumns.CONCRETE_ID + ") FROM "
852f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + Tables.CONTACTS_JOIN_RAW_CONTACTS_DATA_FILTERED_BY_GROUPMEMBERSHIP
853f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + ")")
854f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_WITH_PHONES,
855f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                    "(SELECT COUNT(" + ContactsColumns.CONCRETE_ID + ") FROM "
856f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + Tables.CONTACTS_JOIN_RAW_CONTACTS_DATA_FILTERED_BY_GROUPMEMBERSHIP
857f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + " WHERE " + Contacts.HAS_PHONE_NUMBER + ")")
858f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            .build();
859f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa
860f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa    // This is only exposed as hidden API for the contacts app, so we can be very specific in
861f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa    // the filtering
862f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa    private static final ProjectionMap sGroupsSummaryProjectionMapWithGroupCountPerAccount =
863f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            ProjectionMap.builder()
864f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            .addAll(sGroupsSummaryProjectionMap)
865f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            .add(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT,
866f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                    "(SELECT COUNT(*) FROM " + Views.GROUPS + " WHERE "
867f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + "(" + Groups.ACCOUNT_NAME + "="
868f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + GroupsColumns.CONCRETE_ACCOUNT_NAME
869f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + " AND "
870f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.ACCOUNT_TYPE + "=" + GroupsColumns.CONCRETE_ACCOUNT_TYPE
871f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + " AND "
872f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.DELETED + "=0 AND "
873f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.FAVORITES + "=0 AND "
874f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.AUTO_ADD + "=0"
875f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + ")"
876f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + " GROUP BY "
877f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.ACCOUNT_NAME + ", " + Groups.ACCOUNT_TYPE
878f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                   + ")")
879f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
880f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
881373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains the agg_exceptions columns */
882f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sAggregationExceptionsProjectionMap = ProjectionMap.builder()
883f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id")
884f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.TYPE)
885f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID1)
886f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID2)
887f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
888f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
889eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    /** Contains the agg_exceptions columns */
890f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSettingsProjectionMap = ProjectionMap.builder()
891f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_NAME)
892f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_TYPE)
893f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            .add(Settings.DATA_SET)
894f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_VISIBLE)
895f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.SHOULD_SYNC)
896f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ANY_UNSYNCED,
897f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN MIN(" + Settings.SHOULD_SYNC
898f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                        + ",(SELECT "
899f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + "(CASE WHEN MIN(" + Groups.SHOULD_SYNC + ") IS NULL"
900f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " THEN 1"
901f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " ELSE MIN(" + Groups.SHOULD_SYNC + ")"
902f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " END)"
903f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.GROUPS
904f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
905f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_NAME
906f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
907f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                                    + SettingsColumns.CONCRETE_ACCOUNT_TYPE
908f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                                + " AND ((" + GroupsColumns.CONCRETE_DATA_SET + " IS NULL AND "
909f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                                    + SettingsColumns.CONCRETE_DATA_SET + " IS NULL) OR ("
910f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                                    + GroupsColumns.CONCRETE_DATA_SET + "="
911f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                                    + SettingsColumns.CONCRETE_DATA_SET + "))))=0"
912f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN 1"
913f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE 0"
914f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " END)")
915f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_COUNT,
916f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
917f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
918f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
919f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
920f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
921f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
922f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_WITH_PHONES,
923f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
924f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
925f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
926f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + Contacts.HAS_PHONE_NUMBER
927f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
928f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
929f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
930f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
931f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
93282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Contains StatusUpdates columns */
933f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStatusUpdatesProjectionMap = ProjectionMap.builder()
934f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PresenceColumns.RAW_CONTACT_ID)
935f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.DATA_ID, DataColumns.CONCRETE_ID)
936f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_ACCOUNT)
937f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_HANDLE)
938f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PROTOCOL)
939f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // We cannot allow a null in the custom protocol field, because SQLite3 does not
940f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // properly enforce uniqueness of null values
941f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CUSTOM_PROTOCOL,
942f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN " + StatusUpdates.CUSTOM_PROTOCOL + "=''"
943f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN NULL"
944f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE " + StatusUpdates.CUSTOM_PROTOCOL + " END)")
945f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PRESENCE)
946f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CHAT_CAPABILITY)
947f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS)
948f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_TIMESTAMP)
949f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_RES_PACKAGE)
950f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_ICON)
951f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_LABEL)
952f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
953f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
9543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /** Contains StreamItems columns */
9553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final ProjectionMap sStreamItemsProjectionMap = ProjectionMap.builder()
9569b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems._ID)
9579b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.CONTACT_ID)
958af10329f85c5d8c4196c495a9f0f9a6c6ecbc231Daniel Lehmann            .add(StreamItems.CONTACT_LOOKUP_KEY)
9599b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.ACCOUNT_NAME)
9609b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.ACCOUNT_TYPE)
9619b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.DATA_SET)
9623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RAW_CONTACT_ID)
9639b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.RAW_CONTACT_SOURCE_ID)
9643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_PACKAGE)
9653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_ICON)
9663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_LABEL)
9673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.TEXT)
9683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.TIMESTAMP)
9693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.COMMENTS)
9700bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.SYNC1)
9710bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.SYNC2)
9720bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.SYNC3)
9730bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.SYNC4)
9743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .build();
9753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
9763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final ProjectionMap sStreamItemPhotosProjectionMap = ProjectionMap.builder()
9773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos._ID, StreamItemPhotosColumns.CONCRETE_ID)
9783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RAW_CONTACT_ID)
9790bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.RAW_CONTACT_SOURCE_ID, RawContactsColumns.CONCRETE_SOURCE_ID)
9803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.STREAM_ITEM_ID)
9813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.SORT_INDEX)
9826802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            .add(StreamItemPhotos.PHOTO_FILE_ID)
9836802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            .add(StreamItemPhotos.PHOTO_URI,
9846802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    "'" + DisplayPhoto.CONTENT_URI + "'||'/'||" + StreamItemPhotos.PHOTO_FILE_ID)
9851dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.HEIGHT)
9861dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.WIDTH)
9871dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.FILESIZE)
9880bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItemPhotos.SYNC1)
9890bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItemPhotos.SYNC2)
9900bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItemPhotos.SYNC3)
9910bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItemPhotos.SYNC4)
9923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .build();
9933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
9941b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    /** Contains Live Folders columns */
995f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sLiveFoldersProjectionMap = ProjectionMap.builder()
996f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders._ID, Contacts._ID)
997f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders.NAME, Contacts.DISPLAY_NAME)
998f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // TODO: Put contact photo back when we have a way to display a default icon
999f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // for contacts without a photo
1000f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // .add(LiveFolders.ICON_BITMAP, Photos.DATA)
1001f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
1002f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
1003d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /** Contains {@link Directory} columns */
1004f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDirectoryProjectionMap = ProjectionMap.builder()
1005f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory._ID)
1006f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.PACKAGE_NAME)
1007f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.TYPE_RESOURCE_ID)
1008f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DISPLAY_NAME)
1009f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DIRECTORY_AUTHORITY)
1010f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_TYPE)
1011f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_NAME)
1012f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.EXPORT_SUPPORT)
1013778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.SHORTCUT_SUPPORT)
1014778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.PHOTO_SUPPORT)
1015f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
10167e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
10179705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    // where clause to update the status_updates table
10189705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private static final String WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE =
10199705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdatesColumns.DATA_ID + " IN (SELECT Distinct " + StatusUpdates.DATA_ID +
10209705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " FROM " + Tables.STATUS_UPDATES + " LEFT OUTER JOIN " + Tables.PRESENCE +
10219705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " ON " + StatusUpdatesColumns.DATA_ID + " = " + StatusUpdates.DATA_ID + " WHERE ";
10229705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
10232526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private static final String[] EMPTY_STRING_ARRAY = new String[0];
10242526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
1025bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1026bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Notification ID for failure to import contacts.
1027bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1028bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private static final int LEGACY_IMPORT_FAILED_NOTIFICATION = 1;
102951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
103003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_START_MATCH = "[";
103103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_END_MATCH = "]";
103203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_ELLIPSIS = "...";
103303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final int DEFAULT_SNIPPET_ARG_MAX_TOKENS = -10;
103403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
10359a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    private boolean sIsPhoneInitialized;
10369a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    private boolean sIsPhone;
10379a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
1038f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private StringBuilder mSb = new StringBuilder();
10391129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs1 = new String[1];
10401129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs2 = new String[2];
10412526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private ArrayList<String> mSelectionArgs = Lists.newArrayList();
10422526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
1043f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private Account mAccount;
1044f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
104546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
104646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Stores mapping from type Strings exposed via {@link DataUsageFeedback} to
104746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * type integers in {@link DataUsageStatColumns}.
104846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
104946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final Map<String, Integer> sDataUsageTypeMap;
105046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
10514f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
10524f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
1053a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
1054d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
1055d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
1056a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_ID_DATA);
1057a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/entities", CONTACTS_ID_ENTITIES);
10583653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
10593653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
10602d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*",
10612d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                AGGREGATION_SUGGESTIONS);
1062a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_ID_PHOTO);
1063f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/display_photo",
1064f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_ID_DISPLAY_PHOTO);
10653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/stream_items",
10663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_ID_STREAM_ITEMS);
1067c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter", CONTACTS_FILTER);
10685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER);
10695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP);
10702149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/data", CONTACTS_LOOKUP_DATA);
1071bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/photo",
1072bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                CONTACTS_LOOKUP_PHOTO);
10735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
10742149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/data",
10752149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                CONTACTS_LOOKUP_ID_DATA);
1076bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/photo",
1077bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                CONTACTS_LOOKUP_ID_PHOTO);
1078f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/display_photo",
1079f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_LOOKUP_DISPLAY_PHOTO);
1080f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/display_photo",
1081f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_LOOKUP_ID_DISPLAY_PHOTO);
1082a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/entities",
1083a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ENTITIES);
1084a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/entities",
1085a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ID_ENTITIES);
10863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/stream_items",
10873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_LOOKUP_STREAM_ITEMS);
10883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/stream_items",
10893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_LOOKUP_ID_STREAM_ITEMS);
1090f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_vcard/*", CONTACTS_AS_VCARD);
109142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_multi_vcard/*",
109242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                CONTACTS_AS_MULTI_VCARD);
10935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT);
1094ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*",
1095ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                CONTACTS_STREQUENT_FILTER);
10965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP);
109745ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa        matcher.addURI(ContactsContract.AUTHORITY, "contacts/frequent", CONTACTS_FREQUENT);
10983653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
10995ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
11005ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
11015ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA);
1102f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/display_photo",
1103f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                RAW_CONTACTS_ID_DISPLAY_PHOTO);
110446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ENTITY_ID);
11053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/stream_items",
11063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                RAW_CONTACTS_ID_STREAM_ITEMS);
110782780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/stream_items/#",
110882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                RAW_CONTACTS_ID_STREAM_ITEMS_ID);
110946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
111046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contact_entities", RAW_CONTACT_ENTITIES);
1111b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
11124f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
11134f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
1114ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
111548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID);
11165e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER);
1117ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
11184a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS);
111948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID);
11201dac83b8fa58944acfd00f44e717a7dddc659d2dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup", EMAILS_LOOKUP);
11215e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP);
11225e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER);
11234a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
1124ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
112548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
112646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        /** "*" is in CSV form with data ids ("123,456,789") */
112746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        matcher.addURI(ContactsContract.AUTHORITY, "data/usagefeedback/*", DATA_USAGE_FEEDBACK_ID);
11281f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1129ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
1130ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
1131ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
1132ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
113335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
1134b5a4add17815167d20a90645779df34cdf45280dFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#",
1135b5a4add17815167d20a90645779df34cdf45280dFred Quintana                SYNCSTATE_ID);
11365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/" + SyncStateContentProviderHelper.PATH,
11375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                PROFILE_SYNCSTATE);
11385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY,
11395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                "profile/" + SyncStateContentProviderHelper.PATH + "/#",
11405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                PROFILE_SYNCSTATE_ID);
114135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1142a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
1143b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
1144b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
1145b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
1146b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
11474f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1148eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS);
1149eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
115082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates", STATUS_UPDATES);
115182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates/#", STATUS_UPDATES_ID);
11521f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1153c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
1154c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
1155c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
1156c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
11572d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*",
1158c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SHORTCUT);
1159c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
11601b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts",
11611b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS);
11621b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts/*",
11631b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_GROUP_NAME);
11641b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts_with_phones",
11651b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_WITH_PHONES);
11661b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/favorites",
11671b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_FAVORITES);
116809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
116909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "provider_status", PROVIDER_STATUS);
1170d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
1171d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories", DIRECTORIES);
1172d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories/#", DIRECTORIES_ID);
11737a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
11747a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "complete_name", COMPLETE_NAME);
117524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
117624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile", PROFILE);
117724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/entities", PROFILE_ENTITIES);
117824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/data", PROFILE_DATA);
117924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/data/#", PROFILE_DATA_ID);
118024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/as_vcard", PROFILE_AS_VCARD);
118124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts", PROFILE_RAW_CONTACTS);
118224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#",
118324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID);
118424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/data",
118524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID_DATA);
118624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/entity",
118724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID_ENTITIES);
11885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/status_updates",
11895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                PROFILE_STATUS_UPDATES);
11903202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contact_entities",
11913202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro                PROFILE_RAW_CONTACT_ENTITIES);
119246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
11933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items", STREAM_ITEMS);
11943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/photo", STREAM_ITEMS_PHOTOS);
11953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#", STREAM_ITEMS_ID);
11963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#/photo", STREAM_ITEMS_ID_PHOTOS);
11973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#/photo/#",
11983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                STREAM_ITEMS_ID_PHOTOS_ID);
11993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items_limit", STREAM_ITEMS_LIMIT);
12003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
12015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "display_photo/#", DISPLAY_PHOTO);
1202f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "photo_dimensions", PHOTO_DIMENSIONS);
1203f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
120446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        HashMap<String, Integer> tmpTypeMap = new HashMap<String, Integer>();
120546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_CALL, DataUsageStatColumns.USAGE_TYPE_INT_CALL);
120646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_LONG_TEXT,
120746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT);
120846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_SHORT_TEXT,
120946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DataUsageStatColumns.USAGE_TYPE_INT_SHORT_TEXT);
121046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        sDataUsageTypeMap = Collections.unmodifiableMap(tmpTypeMap);
121119a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    }
121219a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
1213d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static class DirectoryInfo {
1214d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String authority;
1215d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountName;
1216d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountType;
1217d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
1218d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
1219d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
1220d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Cached information about contact directories.
1221d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
12224458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private HashMap<String, DirectoryInfo> mDirectoryCache = new HashMap<String, DirectoryInfo>();
12234458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private boolean mDirectoryCacheValid = false;
1224d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
12253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
122643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * An entry in group id cache. It maps the combination of (account type, account name, data set,
1227ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * and source id) to group row id.
1228ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     */
1229e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    public static class GroupIdCacheEntry {
1230ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType;
1231ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName;
123243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String dataSet;
1233ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String sourceId;
1234ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        long groupId;
1235ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    }
1236a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1237e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // We don't need a soft cache for groups - the assumption is that there will only
1238e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // be a small number of contact groups. The cache is keyed off source id.  The value
1239e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // is a list of groups with this group id.
1240e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    private HashMap<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache = Maps.newHashMap();
1241e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov
124224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
1243f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Maximum dimension (height or width) of display photos.  Larger images will be scaled
1244f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * to fit.
1245f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
1246f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private int mMaxDisplayPhotoDim;
1247f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1248f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
1249f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Maximum dimension (height or width) of photo thumbnails.
1250f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
1251f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private int mMaxThumbnailPhotoDim;
1252f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
12535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
12545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Sub-provider for handling profile requests against the profile database.
12555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
12565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private ProfileProvider mProfileProvider;
1257f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
12584097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
1259f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private NameLookupBuilder mNameLookupBuilder;
1260315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
1261622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private PostalSplitter mPostalSplitter;
1262622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
126372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    private ContactDirectoryManager mContactDirectoryManager;
12645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
12655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
12665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * The active (thread-local) database.  This will be switched between a contacts-specific
12675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * database and a profile-specific database, depending on what the current operation is
12685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * targeted to.
12695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
12705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<SQLiteDatabase> mActiveDb = new ThreadLocal<SQLiteDatabase>();
12715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
12725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // This variable keeps track of whether the current operation is intended for the profile DB.
12735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<Boolean> mInProfileMode = new ThreadLocal<Boolean>();
12745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
12755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Whether we're currently in the process of applying a batch of operations.
12765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<Boolean> mApplyingBatch = new ThreadLocal<Boolean>();
12775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
12785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Separate data row handler instances for contact data and profile data.
12795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private HashMap<String, DataRowHandler> mDataRowHandlers;
12805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private HashMap<String, DataRowHandler> mProfileDataRowHandlers;
12815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
12825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Depending on whether the action being performed is for the profile, we will use one of two
12835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // database helper instances.
12845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<ContactsDatabaseHelper> mDbHelper =
12855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            new ThreadLocal<ContactsDatabaseHelper>();
12865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private ContactsDatabaseHelper mContactsHelper;
12875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private ProfileDatabaseHelper mProfileHelper;
12885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
12895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Depending on whether the action being performed is for the profile or not, we will use one of
12905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // two aggregator instances.
12915d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<ContactAggregator> mAggregator = new ThreadLocal<ContactAggregator>();
1292622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private ContactAggregator mContactAggregator;
12935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private ContactAggregator mProfileAggregator;
12945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
12955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Depending on whether the action being performed is for the profile or not, we will use one of
12965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // two photo store instances (with their files stored in separate subdirectories).
12975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<PhotoStore> mPhotoStore = new ThreadLocal<PhotoStore>();
12985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private PhotoStore mContactsPhotoStore;
12995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private PhotoStore mProfilePhotoStore;
13005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
13015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // The active transaction context will switch depending on the operation being performed.
13025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Both transaction contexts will be cleared out when a batch transaction is started, and
13035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // each will be processed separately when a batch transaction completes.
13045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private TransactionContext mContactTransactionContext = new TransactionContext(false);
13055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private TransactionContext mProfileTransactionContext = new TransactionContext(true);
13065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<TransactionContext> mTransactionContext =
13075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            new ThreadLocal<TransactionContext>();
13085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
13095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // This database reference will only be referenced when a batch operation is in progress
13105d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // that includes profile DB operations.  It is used to create and handle a separate transaction
13115d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // around that batch.  Outside of such a batch operation, this will be null.
13125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<SQLiteDatabase> mProfileDbForBatch =
13135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            new ThreadLocal<SQLiteDatabase>();
13145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
13155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // This flag is set during a batch operation that involves the profile DB to indicate that
13165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // errors occurred during processing of one of the profile operations.
13175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<Boolean> mProfileErrorsInBatch = new ThreadLocal<Boolean>();
13185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
1319f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
1320a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    private GlobalSearchSupport mGlobalSearchSupport;
1321d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov    private CommonNicknameCache mCommonNicknameCache;
1322f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    private SearchIndexManager mSearchIndexManager;
1323a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
132420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private ContentValues mValues = new ContentValues();
132573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    private HashMap<String, Boolean> mAccountWritability = Maps.newHashMap();
132620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
132709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private int mProviderStatus = ProviderStatus.STATUS_NORMAL;
13283826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private boolean mProviderStatusUpdateNeeded;
132909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private long mEstimatedStorageRequirement = 0;
133015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private volatile CountDownLatch mReadAccessLatch;
133115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private volatile CountDownLatch mWriteAccessLatch;
133215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private boolean mAccountUpdateListenerRegistered;
1333bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private boolean mOkToOpenAccess = true;
133473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
13351a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey    private boolean mVisibleTouched = false;
13361a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
133781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    private boolean mSyncToNetwork;
133881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
13394cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    private Locale mCurrentLocale;
13403826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private int mContactsAccountCount;
1341d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov
1342bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private HandlerThread mBackgroundThread;
1343bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private Handler mBackgroundHandler;
1344bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1345f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private long mLastPhotoCleanup = 0;
1346f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
13474f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
13484f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
1349663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki        if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
1350663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki            Log.d(Constants.PERFORMANCE_TAG, "ContactsProvider2.onCreate start");
1351663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki        }
1352de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        super.onCreate();
1353ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        try {
1354ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return initialize();
1355ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        } catch (RuntimeException e) {
1356ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            Log.e(TAG, "Cannot start provider", e);
1357ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return false;
1358663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki        } finally {
1359663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki            if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
1360663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki                Log.d(Constants.PERFORMANCE_TAG, "ContactsProvider2.onCreate finish");
1361663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki            }
1362ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        }
1363ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    }
136435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1365ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    private boolean initialize() {
136615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        StrictMode.setThreadPolicy(
136715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
136815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
13693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Resources resources = getContext().getResources();
1370f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mMaxDisplayPhotoDim = resources.getInteger(
1371f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                R.integer.config_max_display_photo_dim);
1372f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mMaxThumbnailPhotoDim = resources.getInteger(
1373f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                R.integer.config_max_thumbnail_photo_dim);
13743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
13755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsHelper = (ContactsDatabaseHelper) getDatabaseHelper();
13765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mDbHelper.set(mContactsHelper);
137772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        mContactDirectoryManager = new ContactDirectoryManager(this);
1378a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mGlobalSearchSupport = new GlobalSearchSupport(this);
137965ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov
1380bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        // The provider is closed for business until fully initialized
138115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mReadAccessLatch = new CountDownLatch(1);
138215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mWriteAccessLatch = new CountDownLatch(1);
138372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
1384bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundThread = new HandlerThread("ContactsProviderWorker",
1385bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                Process.THREAD_PRIORITY_BACKGROUND);
1386bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundThread.start();
1387bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler = new Handler(mBackgroundThread.getLooper()) {
1388bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            @Override
1389bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            public void handleMessage(Message msg) {
1390bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                performBackgroundTask(msg.what, msg.obj);
1391bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1392bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        };
13932a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov
13945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Set up the sub-provider for handling profiles.
13955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileProvider = getProfileProvider();
13965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        ProviderInfo profileInfo = new ProviderInfo();
13975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        profileInfo.readPermission = "android.permission.READ_PROFILE";
13985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        profileInfo.writePermission = "android.permission.WRITE_PROFILE";
13995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileProvider.attachInfo(getContext(), profileInfo);
14005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileHelper = (ProfileDatabaseHelper) mProfileProvider.getDatabaseHelper();
14015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
140215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_INITIALIZE);
1403bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS);
1404bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
1405bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_LOCALE);
1406bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM);
140705e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_SEARCH_INDEX);
1408bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_PROVIDER_STATUS);
140915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_OPEN_WRITE_ACCESS);
1410f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
14113826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
141249d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov        return true;
14134f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
14144f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1415767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    /**
141651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * (Re)allocates all locale-sensitive structures.
141751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
141804b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov    private void initForDefaultLocale() {
141915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        Context context = getContext();
14205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mLegacyApiSupport = new LegacyApiSupport(context, mContactsHelper, this,
14215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mGlobalSearchSupport);
14224cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mCurrentLocale = getLocale();
14235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mNameSplitter = mContactsHelper.createNameSplitter();
14244cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
14254cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mPostalSplitter = new PostalSplitter(mCurrentLocale);
14265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mCommonNicknameCache = new CommonNicknameCache(mContactsHelper.getReadableDatabase());
1427cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao        ContactLocaleUtils.getIntance().setLocale(mCurrentLocale);
14285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactAggregator = new ContactAggregator(this, mContactsHelper,
142915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                createPhotoPriorityResolver(context), mNameSplitter, mCommonNicknameCache);
14305b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
14315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileAggregator = new ProfileAggregator(this, mProfileHelper,
14325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                createPhotoPriorityResolver(context), mNameSplitter, mCommonNicknameCache);
14335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
1434f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov        mSearchIndexManager = new SearchIndexManager(this);
14355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
14365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsPhotoStore = new PhotoStore(getContext().getFilesDir(), mContactsHelper);
14375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfilePhotoStore = new PhotoStore(new File(getContext().getFilesDir(), "profile"),
14385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mProfileHelper);
14395b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
1440bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers = new HashMap<String, DataRowHandler>();
14415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        initDataRowHandlers(mDataRowHandlers, mContactsHelper, mContactAggregator,
14425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mContactsPhotoStore);
14435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileDataRowHandlers = new HashMap<String, DataRowHandler>();
14445d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        initDataRowHandlers(mProfileDataRowHandlers, mProfileHelper, mProfileAggregator,
14455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mProfilePhotoStore);
14465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
14475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Set initial thread-local state variables for the Contacts DB.
14485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        switchToContactMode();
14495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
1450bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
14515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private void initDataRowHandlers(Map<String, DataRowHandler> handlerMap,
14525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            ContactsDatabaseHelper dbHelper, ContactAggregator contactAggregator,
14535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            PhotoStore photoStore) {
14545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Context context = getContext();
14555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Email.CONTENT_ITEM_TYPE,
14565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForEmail(context, dbHelper, contactAggregator));
14575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Im.CONTENT_ITEM_TYPE,
14585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForIm(context, dbHelper, contactAggregator));
14595d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Organization.CONTENT_ITEM_TYPE,
14605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForOrganization(context, dbHelper, contactAggregator));
14615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Phone.CONTENT_ITEM_TYPE,
14625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForPhoneNumber(context, dbHelper, contactAggregator));
14635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Nickname.CONTENT_ITEM_TYPE,
14645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForNickname(context, dbHelper, contactAggregator));
14655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(StructuredName.CONTENT_ITEM_TYPE,
14665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForStructuredName(context, dbHelper, contactAggregator,
1467bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mNameSplitter, mNameLookupBuilder));
14685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(StructuredPostal.CONTENT_ITEM_TYPE,
14695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForStructuredPostal(context, dbHelper, contactAggregator,
1470bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mPostalSplitter));
14715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(GroupMembership.CONTENT_ITEM_TYPE,
14725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForGroupMembership(context, dbHelper, contactAggregator,
1473bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mGroupIdCache));
14745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Photo.CONTENT_ITEM_TYPE,
14755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForPhoto(context, dbHelper, contactAggregator, photoStore));
14765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Note.CONTENT_ITEM_TYPE,
14775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForNote(context, dbHelper, contactAggregator));
1478bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1479bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1480bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    /**
1481bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     * Visible for testing.
1482bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     */
1483bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    /* package */ PhotoPriorityResolver createPhotoPriorityResolver(Context context) {
1484bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        return new PhotoPriorityResolver(context);
1485bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1486bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1487bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task) {
1488bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendEmptyMessage(task);
1489bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1490bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1491bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task, Object arg) {
1492bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendMessage(mBackgroundHandler.obtainMessage(task, arg));
1493bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1494bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1495bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void performBackgroundTask(int task, Object arg) {
1496bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        switch (task) {
149715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_INITIALIZE: {
149815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                initForDefaultLocale();
149915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch.countDown();
150015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch = null;
150115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                break;
150215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            }
150315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
150415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_OPEN_WRITE_ACCESS: {
1505bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (mOkToOpenAccess) {
150615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch.countDown();
150715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch = null;
1508bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1509bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1510bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1511bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1512bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS: {
1513bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (isLegacyContactImportNeeded()) {
1514bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    importLegacyContactsInBackground();
1515bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1516bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1517bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1518bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1519bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_ACCOUNTS: {
152015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Context context = getContext();
152115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                if (!mAccountUpdateListenerRegistered) {
152215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    AccountManager.get(context).addOnAccountsUpdatedListener(this, null, false);
152315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mAccountUpdateListenerRegistered = true;
152415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                }
152515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
15265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                // Update the accounts for both the contacts and profile DBs.
152715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Account[] accounts = AccountManager.get(context).getAccounts();
15285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                switchToContactMode();
1529bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                boolean accountsChanged = updateAccountsInBackground(accounts);
15305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                switchToProfileMode();
15315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                accountsChanged |= updateAccountsInBackground(accounts);
15325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
1533bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateContactsAccountCount(accounts);
1534bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateDirectoriesInBackground(accountsChanged);
1535bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1536bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1537bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1538bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_LOCALE: {
1539bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateLocaleInBackground();
1540bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1541bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1542bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1543fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            case BACKGROUND_TASK_CHANGE_LOCALE: {
1544fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                changeLocaleInBackground();
1545fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                break;
1546fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            }
1547fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1548bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM: {
1549bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (isAggregationUpgradeNeeded()) {
1550bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    upgradeAggregationAlgorithmInBackground();
1551bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1552bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1553bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1554bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
155505e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_SEARCH_INDEX: {
155605e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                updateSearchIndexInBackground();
155705e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                break;
155805e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            }
155905e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
1560bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_PROVIDER_STATUS: {
1561bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateProviderStatus();
1562bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1563bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1564bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1565bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_DIRECTORIES: {
1566bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (arg != null) {
1567bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    mContactDirectoryManager.onPackageChanged((String) arg);
1568bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1569bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1570bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1571f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1572f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case BACKGROUND_TASK_CLEANUP_PHOTOS: {
1573f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Check rate limit.
1574f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long now = System.currentTimeMillis();
1575f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (now - mLastPhotoCleanup > PHOTO_CLEANUP_RATE_LIMIT) {
1576f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    mLastPhotoCleanup = now;
15775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
15785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    // Clean up photo stores for both contacts and profiles.
15795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    switchToContactMode();
15805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    cleanupPhotoStore();
15815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    switchToProfileMode();
1582f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    cleanupPhotoStore();
1583f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    break;
1584f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
1585f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1586bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
15874cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
15884cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao
158953fac8f99f3884c372c907a76766d27fa9e1d95fDmitri Plotnikov    public void onLocaleChanged() {
15903826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
15913826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
15924f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov            return;
15934f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov        }
15944f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov
1595fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_CHANGE_LOCALE);
15964cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
159751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
159851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    /**
159951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * Verifies that the contacts database is properly configured for the current locale.
160051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * If not, changes the database locale to the current locale using an asynchronous task.
160151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * This needs to be done asynchronously because the process involves rebuilding
160251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * large data structures (name lookup, sort keys), which can take minutes on
160351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * a large set of contacts.
160451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
1605bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateLocaleInBackground() {
1606f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
1607f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        // The process is already running - postpone the change
1608f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        if (mProviderStatus == ProviderStatus.STATUS_CHANGING_LOCALE) {
1609f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov            return;
1610f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        }
1611f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
161251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
161351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final String providerLocale = prefs.getString(PREF_LOCALE, null);
161451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final Locale currentLocale = mCurrentLocale;
161551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        if (currentLocale.toString().equals(providerLocale)) {
161651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            return;
161751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        }
161851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
161951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        int providerStatus = mProviderStatus;
162051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_CHANGING_LOCALE);
16215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsHelper.setLocale(this, currentLocale);
16225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileHelper.setLocale(this, currentLocale);
1623bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        prefs.edit().putString(PREF_LOCALE, currentLocale.toString()).apply();
1624bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        setProviderStatus(providerStatus);
1625bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
162651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
1627fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    /**
1628fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     * Reinitializes the provider for a new locale.
1629fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     */
1630fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    private void changeLocaleInBackground() {
1631fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Re-initializing the provider without stopping it.
1632fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Locking the database will prevent inserts/updates/deletes from
1633fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // running at the same time, but queries may still be running
1634fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // on other threads. Those queries may return inconsistent results.
16355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        SQLiteDatabase db = mContactsHelper.getWritableDatabase();
16365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        SQLiteDatabase profileDb = mProfileHelper.getWritableDatabase();
1637fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        db.beginTransaction();
16385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        profileDb.beginTransaction();
1639fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        try {
1640fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            initForDefaultLocale();
1641fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.setTransactionSuccessful();
16425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            profileDb.setTransactionSuccessful();
1643fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        } finally {
1644fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.endTransaction();
16455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            profileDb.endTransaction();
1646fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        }
1647fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1648fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        updateLocaleInBackground();
1649fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    }
1650fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
165105e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    protected void updateSearchIndexInBackground() {
165205e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        mSearchIndexManager.updateIndex();
165305e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    }
165405e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
1655bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateDirectoriesInBackground(boolean rescan) {
1656bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mContactDirectoryManager.scanAllPackages(rescan);
165751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
165851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
16593826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateProviderStatus() {
16603826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
16613826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
16623826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return;
16633826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
16643826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
16653e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson        // No accounts/no contacts status is true if there are no account and
16665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // there are no contacts or one profile contact
16673e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson        if (mContactsAccountCount == 0) {
16685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            long contactsNum = DatabaseUtils.queryNumEntries(mContactsHelper.getReadableDatabase(),
16693e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                    Tables.CONTACTS, null);
16705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            long profileNum = DatabaseUtils.queryNumEntries(mProfileHelper.getReadableDatabase(),
16715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Tables.CONTACTS, null);
16725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
16735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            // TODO: Different status if there is a profile but no contacts?
16745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (contactsNum == 0 && profileNum <= 1) {
16753e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                setProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS);
16763e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson            } else {
16773e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                setProviderStatus(ProviderStatus.STATUS_NORMAL);
16783e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson            }
16793826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } else {
16803826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            setProviderStatus(ProviderStatus.STATUS_NORMAL);
16813826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
16823826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
16833826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
168431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
1685f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    protected void cleanupPhotoStore() {
16865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
16876802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
16886802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Assemble the set of photo store file IDs that are in use, and send those to the photo
1689f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // store.  Any photos that aren't in that set will be deleted, and any photos that no
1690f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // longer exist in the photo store will be returned for us to clear out in the DB.
16917cf50494501938f175d288077145acf49da8f171Daniel Lehmann        long photoMimeTypeId = mDbHelper.get().getMimeTypeId(Photo.CONTENT_ITEM_TYPE);
16926802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Cursor c = db.query(Views.DATA, new String[]{Data._ID, Photo.PHOTO_FILE_ID},
16937cf50494501938f175d288077145acf49da8f171Daniel Lehmann                DataColumns.MIMETYPE_ID + "=" + photoMimeTypeId + " AND "
1694f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        + Photo.PHOTO_FILE_ID + " IS NOT NULL", null, null, null, null);
16956802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Set<Long> usedPhotoFileIds = Sets.newHashSet();
16966802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> photoFileIdToDataId = Maps.newHashMap();
1697f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        try {
1698f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            while (c.moveToNext()) {
16996802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long dataId = c.getLong(0);
17006802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long photoFileId = c.getLong(1);
17016802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                usedPhotoFileIds.add(photoFileId);
17026802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                photoFileIdToDataId.put(photoFileId, dataId);
17036802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
17046802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        } finally {
17056802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            c.close();
17066802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
17076802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
17086802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Also query for all social stream item photos.
1709c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro        c = db.query(Tables.STREAM_ITEM_PHOTOS + " JOIN " + Tables.STREAM_ITEMS
1710c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                + " ON " + StreamItemPhotos.STREAM_ITEM_ID + "=" + StreamItemsColumns.CONCRETE_ID
1711c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                + " JOIN " + Tables.RAW_CONTACTS
1712c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                + " ON " + StreamItems.RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID,
17136802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                new String[]{
1714c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                        StreamItemPhotosColumns.CONCRETE_ID,
1715c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                        StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID,
1716c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                        StreamItemPhotos.PHOTO_FILE_ID,
1717c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                        RawContacts.ACCOUNT_TYPE,
1718c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                        RawContacts.ACCOUNT_NAME
17196802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                },
17206802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                null, null, null, null, null);
17216802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> photoFileIdToStreamItemPhotoId = Maps.newHashMap();
17226802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> streamItemPhotoIdToStreamItemId = Maps.newHashMap();
1723c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro        Map<Long, Account> streamItemPhotoIdToAccount = Maps.newHashMap();
17246802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        try {
17256802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            while (c.moveToNext()) {
17266802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long streamItemPhotoId = c.getLong(0);
17276802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long streamItemId = c.getLong(1);
17286802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long photoFileId = c.getLong(2);
1729c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                String accountType = c.getString(3);
1730c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                String accountName = c.getString(4);
17316802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                usedPhotoFileIds.add(photoFileId);
17326802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                photoFileIdToStreamItemPhotoId.put(photoFileId, streamItemPhotoId);
17336802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                streamItemPhotoIdToStreamItemId.put(streamItemPhotoId, streamItemId);
1734c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                Account account = new Account(accountName, accountType);
1735c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                streamItemPhotoIdToAccount.put(photoFileId, account);
1736f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1737f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } finally {
1738f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            c.close();
1739f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
1740f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1741f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // Run the photo store cleanup.
17425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> missingPhotoIds = mPhotoStore.get().cleanup(usedPhotoFileIds);
1743f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1744f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // If any of the keys we're using no longer exist, clean them up.
17456802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (!missingPhotoIds.isEmpty()) {
1746f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            ArrayList<ContentProviderOperation> ops = Lists.newArrayList();
17476802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            for (long missingPhotoId : missingPhotoIds) {
17486802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (photoFileIdToDataId.containsKey(missingPhotoId)) {
17496802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    long dataId = photoFileIdToDataId.get(missingPhotoId);
1750f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    ContentValues updateValues = new ContentValues();
1751f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    updateValues.putNull(Photo.PHOTO_FILE_ID);
1752f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    ops.add(ContentProviderOperation.newUpdate(
17536802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            ContentUris.withAppendedId(Data.CONTENT_URI, dataId))
1754f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            .withValues(updateValues).build());
1755f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
17566802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (photoFileIdToStreamItemPhotoId.containsKey(missingPhotoId)) {
17576802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // For missing photos that were in stream item photos, just delete the stream
17586802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // item photo.
17596802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    long streamItemPhotoId = photoFileIdToStreamItemPhotoId.get(missingPhotoId);
17606802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    long streamItemId = streamItemPhotoIdToStreamItemId.get(streamItemPhotoId);
1761c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                    Account account = streamItemPhotoIdToAccount.get(missingPhotoId);
17626802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    ops.add(ContentProviderOperation.newDelete(
17636802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            StreamItems.CONTENT_URI.buildUpon()
17646802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    .appendPath(String.valueOf(streamItemId))
17656802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    .appendPath(StreamItems.StreamItemPhotos.CONTENT_DIRECTORY)
17666802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    .appendPath(String.valueOf(streamItemPhotoId))
1767c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                                    .appendQueryParameter(RawContacts.ACCOUNT_NAME, account.name)
1768c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                                    .appendQueryParameter(RawContacts.ACCOUNT_TYPE, account.type)
17696802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    .build()).build());
17706802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                }
1771f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1772f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            try {
1773f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                applyBatch(ops);
1774f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            } catch (OperationApplicationException oae) {
1775f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Not a fatal problem (and we'll try again on the next cleanup).
1776f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                Log.e(TAG, "Failed to clean up outdated photo references", oae);
1777f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1778f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
1779f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
1780f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1781f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /* Visible for testing */
1782de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    @Override
1783b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
1784b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        return ContactsDatabaseHelper.getInstance(context);
178531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
178631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
17875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    public ProfileProvider getProfileProvider() {
17885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return new ProfileProvider(this);
17895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
17905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
1791524913c66ce75ca8dec127ac88e3bc2249c246d9Dave Santoro    @VisibleForTesting
1792f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /* package */ PhotoStore getPhotoStore() {
17935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mContactsPhotoStore;
1794f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
1795f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
179687614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    /* package */ int getMaxDisplayPhotoDim() {
179787614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro        return mMaxDisplayPhotoDim;
179887614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    }
179987614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro
180087614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    /* package */ int getMaxThumbnailPhotoDim() {
180187614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro        return mMaxThumbnailPhotoDim;
180287614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    }
180387614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro
1804013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    /* package */ NameSplitter getNameSplitter() {
1805013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        return mNameSplitter;
1806013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    }
1807013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov
18085df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    /* package */ NameLookupBuilder getNameLookupBuilder() {
18095df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov        return mNameLookupBuilder;
18105df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    }
18115df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov
18125dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    /* Visible for testing */
1813ed78fd6df5e9f3a2d572162e5d374d1f4a625bddDmitri Plotnikov    public ContactDirectoryManager getContactDirectoryManagerForTest() {
181472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        return mContactDirectoryManager;
181572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
181672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
181772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    /* Visible for testing */
18185dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    protected Locale getLocale() {
18195dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        return Locale.getDefault();
18205dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
18215dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
18225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private boolean applyingBatch() {
18235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Boolean applyingBatch = mApplyingBatch.get();
18245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return applyingBatch != null && applyingBatch;
18255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
18265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
18275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private boolean inProfileMode() {
18285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Boolean profileMode = mInProfileMode.get();
18295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return profileMode != null && profileMode;
18305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
18315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
18323d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    protected boolean isLegacyContactImportNeeded() {
18335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int version = Integer.parseInt(
18345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mContactsHelper.getProperty(PROPERTY_CONTACTS_IMPORTED, "0"));
1835b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        return version < PROPERTY_CONTACTS_IMPORT_VERSION;
18363d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
18373d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1838568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected LegacyContactImporter getLegacyContactImporter() {
1839568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return new LegacyContactImporter(getContext(), this);
1840568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1841568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1842568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
1843bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     * Imports legacy contacts as a background task.
1844568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
1845bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private void importLegacyContactsInBackground() {
1846bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Importing legacy contacts");
1847bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADING);
1848568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1849bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
18505d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsHelper.setLocale(this, mCurrentLocale);
1851bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        prefs.edit().putString(PREF_LOCALE, mCurrentLocale.toString()).commit();
1852568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1853bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        LegacyContactImporter importer = getLegacyContactImporter();
1854bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        if (importLegacyContacts(importer)) {
1855bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            onLegacyContactImportSuccess();
1856bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        } else {
1857bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            onLegacyContactImportFailure();
1858bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
1859568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1860568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1861bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1862bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Unlocks the provider and declares that the import process is complete.
1863bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1864bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportSuccess() {
1865bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
1866bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)getContext().getSystemService(Context.NOTIFICATION_SERVICE);
1867bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.cancel(LEGACY_IMPORT_FAILED_NOTIFICATION);
1868bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1869b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        // Store a property in the database indicating that the conversion process succeeded
18705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsHelper.setProperty(PROPERTY_CONTACTS_IMPORTED,
1871b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov                String.valueOf(PROPERTY_CONTACTS_IMPORT_VERSION));
1872bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_NORMAL);
1873bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Completed import of legacy contacts");
1874bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    }
1875bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1876bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1877bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Announces the provider status and keeps the provider locked.
1878bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1879bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportFailure() {
1880bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Context context = getContext();
1881bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
1882bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
1883bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1884bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        // Show a notification
1885bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Notification n = new Notification(android.R.drawable.stat_notify_error,
1886bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_ticker),
1887bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                System.currentTimeMillis());
1888bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.setLatestEventInfo(context,
1889bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_title),
1890bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_text),
1891bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                PendingIntent.getActivity(context, 0, new Intent(Intents.UI.LIST_DEFAULT), 0));
1892bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
1893bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1894bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.notify(LEGACY_IMPORT_FAILED_NOTIFICATION, n);
1895bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1896bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADE_OUT_OF_MEMORY);
1897bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Failed to import legacy contacts");
1898bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1899bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        // Do not let any database changes until this issue is resolved.
1900bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mOkToOpenAccess = false;
19013d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
19023d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
19033d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /* Visible for testing */
1904568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
19050e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        boolean aggregatorEnabled = mContactAggregator.isEnabled();
19063d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mContactAggregator.setEnabled(false);
19073d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        try {
1908bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            if (importer.importContacts()) {
1909bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1910bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                // TODO aggregate all newly added raw contacts
1911bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                mContactAggregator.setEnabled(aggregatorEnabled);
1912bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                return true;
1913bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
19143d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } catch (Throwable e) {
19153d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           Log.e(TAG, "Legacy contact import failed", e);
19163d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
1917bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mEstimatedStorageRequirement = importer.getEstimatedStorageRequirement();
1918bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        return false;
19193d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
19203d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1921a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
1922a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
1923a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
1924a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
19255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsHelper.wipeData();
19265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileHelper.wipeData();
19275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsPhotoStore.clear();
19285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfilePhotoStore.clear();
19293826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatus = ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS;
1930a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1931a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1932568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
193315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov     * During intialization, this content provider will
1934568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * block all attempts to change contacts data. In particular, it will hold
1935568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * up all contact syncs. As soon as the import process is complete, all
1936568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * processes waiting to write to the provider are unblocked and can proceed
1937568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * to compete for the database transaction monitor.
1938568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
193915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private void waitForAccess(CountDownLatch latch) {
194015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (latch == null) {
194115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            return;
194215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        }
194315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
194415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        while (true) {
194515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            try {
194615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                latch.await();
194715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                return;
194815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            } catch (InterruptedException e) {
194915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Thread.currentThread().interrupt();
1950ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            }
1951568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
1952568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1953568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
19545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
19555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Determines whether the given URI should be directed to the profile
19565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * database rather than the contacts database.  This is true under either
19575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * of three conditions:
19585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * 1. The URI itself is specifically for the profile.
19595d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * 2. The URI contains ID references that are in the profile ID-space.
19605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * 3. The URI contains lookup key references that match the special profile lookup key.
19615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @param uri The URI to examine.
19625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @return Whether to direct the DB operation to the profile database.
19635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
19645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private boolean mapsToProfileDb(Uri uri) {
19655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return sUriMatcher.mapsToProfile(uri);
19665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
19675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
19685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
19695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Determines whether the given URI with the given values being inserted
19705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * should be directed to the profile database rather than the contacts
19715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * database.  This is true if the URI already maps to the profile DB from
19725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * a call to {@link #mapsToProfileDb} or if the URI matches a URI that
19735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * specifies parent IDs via the ContentValues, and the given ContentValues
19745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * contains an ID in the profile ID-space.
19755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @param uri The URI to examine.
19765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @param values The values being inserted.
19775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @return Whether to direct the DB insert to the profile database.
19785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
19795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private boolean mapsToProfileDbWithInsertedValues(Uri uri, ContentValues values) {
19805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDb(uri)) {
19815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return true;
19825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
19835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int match = sUriMatcher.match(uri);
19845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (INSERT_URI_ID_VALUE_MAP.containsKey(match)) {
19855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            String idField = INSERT_URI_ID_VALUE_MAP.get(match);
19865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (values.containsKey(idField)) {
19875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long id = values.getAsLong(idField);
19885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                if (ContactsContract.isProfileId(id)) {
19895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    return true;
19905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                }
19915d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
19925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
19935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return false;
19945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
19955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
19965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
19975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Switches the provider's thread-local context variables to prepare for performing
19985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * a profile operation.
19995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
20005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private void switchToProfileMode() {
20015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mDbHelper.set(mProfileHelper);
20025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mTransactionContext.set(mProfileTransactionContext);
20035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.set(mProfileAggregator);
20045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mPhotoStore.set(mProfilePhotoStore);
20055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mInProfileMode.set(true);
20065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
20075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // If we're in batch mode and don't yet have a database set up for our transaction,
20085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // get one and start a transaction now.
20095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (applyingBatch() && mProfileDbForBatch.get() == null) {
20105d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            SQLiteDatabase profileDb = mProfileHelper.getWritableDatabase();
20115d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            profileDb.beginTransactionWithListener(this);
20125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mProfileDbForBatch.set(profileDb);
20135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
20145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
20155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
20165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
20175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Switches the provider's thread-local context variables to prepare for performing
20185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * a contacts operation.
20195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
20205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private void switchToContactMode() {
20215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mDbHelper.set(mContactsHelper);
20225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mTransactionContext.set(mContactTransactionContext);
20235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.set(mContactAggregator);
20245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mPhotoStore.set(mContactsPhotoStore);
20255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mInProfileMode.set(false);
20265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
20275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // If not in batch mode, clear out the active database - it will be set to the default
20285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // instance from SQLiteContentProvider if necessary.
20295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (!applyingBatch()) {
20305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.set(null);
20315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
20325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
20335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
2034568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2035568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
203615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
20375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDbWithInsertedValues(uri, values)) {
2038072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro            if (applyingBatch()) {
2039072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                switchToProfileMode();
2040072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                return mProfileProvider.insert(uri, values);
2041072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro            } else {
2042072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                // Start a contacts DB transaction to maintain provider synchronization.
2043072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                SQLiteDatabase contactsDb = mContactsHelper.getWritableDatabase();
2044072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                contactsDb.beginTransactionWithListener(this);
2045072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                Uri result = null;
2046072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                try {
2047072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    // Now switch to profile mode and proceed with the insert using its provider.
2048072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    switchToProfileMode();
2049072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    result = mProfileProvider.insert(uri, values);
2050072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro
2051072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    contactsDb.setTransactionSuccessful();
2052072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                } finally {
2053072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    // Finish the contacts transaction, allowing other provider operations to
2054072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    // proceed.
2055072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    contactsDb.endTransaction();
2056072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                }
2057072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                return result;
2058072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro            }
20595d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } else {
20605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToContactMode();
20615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return super.insert(uri, values);
20625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
2063568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2064568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2065568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2066568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
206715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (mWriteAccessLatch != null) {
2068bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // We are stuck trying to upgrade contacts db.  The only update request
2069bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // allowed in this case is an update of provider status, which will trigger
2070bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // an attempt to upgrade contacts again.
2071bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            int match = sUriMatcher.match(uri);
2072bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            if (match == PROVIDER_STATUS) {
2073bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                Integer newStatus = values.getAsInteger(ProviderStatus.STATUS);
2074bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                if (newStatus != null && newStatus == ProviderStatus.STATUS_UPGRADING) {
2075bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    scheduleBackgroundTask(BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS);
2076bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 1;
2077bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                } else {
2078bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 0;
2079bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                }
2080bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
2081bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
208215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
20835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDb(uri)) {
2084072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro            if (applyingBatch()) {
2085072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                switchToProfileMode();
2086072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                return mProfileProvider.update(uri, values, selection, selectionArgs);
2087072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro            } else {
2088072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                // Start a contacts DB transaction to maintain provider synchronization.
2089072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                SQLiteDatabase contactsDb = mContactsHelper.getWritableDatabase();
2090072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                contactsDb.beginTransactionWithListener(this);
2091072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                int result = 0;
2092072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                try {
2093072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    // Now switch to profile mode and proceed with the update using its provider.
2094072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    switchToProfileMode();
2095072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    result = mProfileProvider.update(uri, values, selection, selectionArgs);
2096072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro
2097072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    contactsDb.setTransactionSuccessful();
2098072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                } finally {
2099072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    // Finish the contacts transaction, allowing other provider operations to
2100072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    // proceed.
2101072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    contactsDb.endTransaction();
2102072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                }
2103072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                return result;
2104072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro            }
21055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } else {
21065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToContactMode();
21075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return super.update(uri, values, selection, selectionArgs);
21085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
2109568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2110568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2111568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2112568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
211315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
21145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDb(uri)) {
2115072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro            if (applyingBatch()) {
2116072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                switchToProfileMode();
2117072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                return mProfileProvider.delete(uri, selection, selectionArgs);
2118072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro            } else {
2119072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                // Start a contacts DB transaction to maintain provider synchronization.
2120072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                SQLiteDatabase contactsDb = mContactsHelper.getWritableDatabase();
2121072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                contactsDb.beginTransactionWithListener(this);
2122072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                int result = 0;
2123072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                try {
2124072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    // Now switch to profile mode and proceed with the delete using its provider.
2125072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    switchToProfileMode();
2126072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    result = mProfileProvider.delete(uri, selection, selectionArgs);
2127072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro
2128072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    contactsDb.setTransactionSuccessful();
2129072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                } finally {
2130072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    // Finish the contacts transaction, allowing other provider operations to
2131072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    // proceed.
2132072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    contactsDb.endTransaction();
2133072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                }
2134072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                return result;
2135072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro            }
21365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } else {
21375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToContactMode();
21385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return super.delete(uri, selection, selectionArgs);
21395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
21405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
21415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
21425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
21435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Replaces the current (thread-local) database to use for the operation with the given one.
21445d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @param db The database to use.
21455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
21465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /* package */ void substituteDb(SQLiteDatabase db) {
21475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.set(db);
2148568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2149568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2150568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2151568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
2152568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            throws OperationApplicationException {
215315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
21545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        ContentProviderResult[] results = null;
21555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        try {
2156d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            mApplyingBatch.set(true);
21575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            results = super.applyBatch(operations);
21585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } finally {
2159d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            mApplyingBatch.set(false);
21605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (mProfileDbForBatch.get() != null) {
21615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                // A profile operation was involved, so clean up its transaction.
21625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                boolean profileErrors = mProfileErrorsInBatch.get() != null
21635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        && mProfileErrorsInBatch.get();
21645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                if (!profileErrors) {
21655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mProfileDbForBatch.get().setTransactionSuccessful();
21665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                }
21675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mProfileDbForBatch.get().endTransaction();
21685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mProfileDbForBatch.set(null);
21695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mProfileErrorsInBatch.set(false);
21705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
21715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
21725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return results;
2173568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2174568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
21754f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
21767b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    public int bulkInsert(Uri uri, ContentValues[] values) {
21777b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov        waitForAccess(mWriteAccessLatch);
2178720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro
2179720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro        // Note: This duplicates much of the logic in the superclass, but handles toggling
2180720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro        // into profile mode if necessary.
2181720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro        int numValues = values.length;
2182720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro        boolean notifyChange = false;
2183720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro        SQLiteDatabase profileDb = null;
2184072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro
2185072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro        // Always get a contacts DB and start a transaction on it, to maintain provider
2186072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro        // synchronization.
21878d55bbc171bea632e960e0e5a88557cef5e66d92Dave Santoro        mDb = mContactsHelper.getWritableDatabase();
21888d55bbc171bea632e960e0e5a88557cef5e66d92Dave Santoro        mDb.beginTransactionWithListener(this);
2189720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro        try {
2190720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro            for (int i = 0; i < numValues; i++) {
2191720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                Uri result;
2192720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                if (mapsToProfileDbWithInsertedValues(uri, values[i])) {
2193720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                    switchToProfileMode();
2194072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro
2195072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    // Initialize the profile DB and start a profile transaction if we haven't
2196072079319b9bd2998b06a2cab0e9b4af6eb05c33Dave Santoro                    // already done so.
2197720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                    if (profileDb == null) {
2198720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                        profileDb = mProfileHelper.getWritableDatabase();
2199720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                        profileDb.beginTransactionWithListener(this);
2200720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                    }
2201720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                    result = mProfileProvider.insertInTransaction(uri, values[i]);
2202720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                } else {
2203720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                    switchToContactMode();
2204720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                    result = insertInTransaction(uri, values[i]);
2205720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                }
2206720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                if (result != null) {
2207720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                    notifyChange = true;
2208720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                }
2209720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                boolean savedNotifyChange = notifyChange;
2210720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                mActiveDb.get().yieldIfContendedSafely();
2211720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                notifyChange = savedNotifyChange;
2212720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro            }
22138d55bbc171bea632e960e0e5a88557cef5e66d92Dave Santoro            mDb.setTransactionSuccessful();
2214720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro            if (profileDb != null) {
2215720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                profileDb.setTransactionSuccessful();
2216720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro            }
2217720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro        } finally {
22188d55bbc171bea632e960e0e5a88557cef5e66d92Dave Santoro            mDb.endTransaction();
2219720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro            if (profileDb != null) {
2220720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro                profileDb.endTransaction();
2221720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro            }
2222720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro        }
2223720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro
2224720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro        if (notifyChange) {
2225720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro            notifyChange();
2226720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro        }
2227720277eff4ffa6344ce2efc1fc39f9ced8af0b6bDave Santoro        return numValues;
22287b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    }
22297b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov
22307b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    @Override
2231285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void onBeginTransaction() {
2232bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2233b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "onBeginTransaction");
2234b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2235285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.onBeginTransaction();
22365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (inProfileMode()) {
22375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mProfileAggregator.clearPendingAggregations();
22385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mProfileTransactionContext.clear();
22395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } else {
22405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mContactAggregator.clearPendingAggregations();
22415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mContactTransactionContext.clear();
22425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
2243b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2244b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2245285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2246285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2247285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void beforeTransactionCommit() {
22481129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
2249bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2250b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "beforeTransactionCommit");
2251b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2252285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.beforeTransactionCommit();
2253b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
22545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().aggregateInTransaction(mTransactionContext.get(), mActiveDb.get());
22551a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (mVisibleTouched) {
22561a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = false;
22575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mDbHelper.get().updateAllVisible();
22581a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        }
22593826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
2260bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        updateSearchIndexInTransaction();
2261bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov
22623826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatusUpdateNeeded) {
22633826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
22643826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatusUpdateNeeded = false;
22653826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
2266b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2267b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2268bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov    private void updateSearchIndexInTransaction() {
22695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> staleContacts = mTransactionContext.get().getStaleSearchIndexContactIds();
22705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> staleRawContacts = mTransactionContext.get().getStaleSearchIndexRawContactIds();
2271bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        if (!staleContacts.isEmpty() || !staleRawContacts.isEmpty()) {
2272bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mSearchIndexManager.updateIndexForRawContacts(staleContacts, staleRawContacts);
22735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mTransactionContext.get().clearSearchIndexUpdates();
2274bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        }
2275bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov    }
2276bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov
2277b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void flushTransactionalChanges() {
2278bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2279b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "flushTransactionChanges");
2280b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
22811129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
22825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        for (long rawContactId : mTransactionContext.get().getInsertedRawContactIds()) {
22835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mDbHelper.get().updateRawContactDisplayName(mActiveDb.get(), rawContactId);
22845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mAggregator.get().onRawContactInsert(mTransactionContext.get(), mActiveDb.get(),
22855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    rawContactId);
228624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
228724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
22885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> dirtyRawContacts = mTransactionContext.get().getDirtyRawContactIds();
2289d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!dirtyRawContacts.isEmpty()) {
2290a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
2291a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_DIRTY_SQL);
2292d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, dirtyRawContacts);
2293a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
22945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().execSQL(mSb.toString());
2295a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        }
2296a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
22975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> updatedRawContacts = mTransactionContext.get().getUpdatedRawContactIds();
2298d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!updatedRawContacts.isEmpty()) {
2299a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
2300a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_VERSION_SQL);
2301d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, updatedRawContacts);
2302a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
23035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().execSQL(mSb.toString());
2304b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2305b5a4add17815167d20a90645779df34cdf45280dFred Quintana
23065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Update sync states.
23075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        for (Map.Entry<Long, Object> entry : mTransactionContext.get().getUpdatedSyncStates()) {
2308b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long id = entry.getKey();
23095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (mDbHelper.get().getSyncState().update(mActiveDb.get(), id, entry.getValue()) <= 0) {
23109d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                throw new IllegalStateException(
23119d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                        "unable to update sync state, does it still exist?");
23129d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            }
2313b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2314b5a4add17815167d20a90645779df34cdf45280dFred Quintana
23155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mTransactionContext.get().clear();
2316b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2317b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2318a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /**
2319a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * Appends comma separated ids.
2320a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * @param ids Should not be empty
2321a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     */
2322d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov    private void appendIds(StringBuilder sb, Set<Long> ids) {
2323b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long id : ids) {
2324a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            sb.append(id).append(',');
2325b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2326a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
2327a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        sb.setLength(sb.length() - 1); // Yank the last comma
2328285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
2329285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2330285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2331cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    protected void notifyChange() {
233281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        notifyChange(mSyncToNetwork);
233381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = false;
233481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
233581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
233681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    protected void notifyChange(boolean syncToNetwork) {
233781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
233881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                syncToNetwork);
2339cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    }
2340568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
234151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    protected void setProviderStatus(int status) {
23423826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != status) {
23433826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatus = status;
23443826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            getContext().getContentResolver().notifyChange(ProviderStatus.CONTENT_URI, null, false);
23453826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
234651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
234751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
2348f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    public DataRowHandler getDataRowHandler(final String mimeType) {
23495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (inProfileMode()) {
23505d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return getDataRowHandlerForProfile(mimeType);
23515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
23523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
23533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
23546d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov            handler = new DataRowHandlerForCustomMimetype(
23555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    getContext(), mContactsHelper, mContactAggregator, mimeType);
23563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
23573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
23583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
23593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
23603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
23615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    public DataRowHandler getDataRowHandlerForProfile(final String mimeType) {
23625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        DataRowHandler handler = mProfileDataRowHandlers.get(mimeType);
23635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (handler == null) {
23645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            handler = new DataRowHandlerForCustomMimetype(
23655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    getContext(), mProfileHelper, mProfileAggregator, mimeType);
23665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mProfileDataRowHandlers.put(mimeType, handler);
23675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
23685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return handler;
23695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
23705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
23714f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2372de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected Uri insertInTransaction(Uri uri, ContentValues values) {
2373bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
23741129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Log.v(TAG, "insertInTransaction: " + uri + " " + values);
2375b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2376f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
23775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
23785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
23795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.set(mDb);
23805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
23815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
2382f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2383f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2384f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2385a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
2386a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
238735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2388a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
238935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
23905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE:
23915d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                id = mDbHelper.get().getSyncState().insert(mActiveDb.get(), values);
239235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
239335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2394d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
2395d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                insertContact(values);
23966bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
23976bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
23986bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
239924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
240024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                throw new UnsupportedOperationException(
240124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        "The profile contact is created automatically");
240224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
240324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2404d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS:
2405d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS: {
24065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                id = insertRawContact(uri, values, callerIsSyncAdapter);
2407f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2408a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2409a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2410a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2411d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS_DATA:
2412d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID_DATA: {
2413d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                int segment = match == RAW_CONTACTS_DATA ? 1 : 2;
2414d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(segment));
2415f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2416f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2417a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2418a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2419a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
24203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case RAW_CONTACTS_ID_STREAM_ITEMS: {
24213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                values.put(StreamItems.RAW_CONTACT_ID, uri.getPathSegments().get(1));
24223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItem(uri, values);
24233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
24243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
24253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
24263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24270c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case DATA:
24280c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case PROFILE_DATA: {
2429f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2430f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2431a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2432a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2433a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2434ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
2435f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                id = insertGroup(uri, values, callerIsSyncAdapter);
2436f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2437ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2438ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2439ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2440eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
24415aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertSettings(uri, values);
244243880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2443eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
2444eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2445eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
24465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case STATUS_UPDATES:
24475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_STATUS_UPDATES: {
244882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                id = insertStatusUpdate(values);
24491f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
24501f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
24511f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
24523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
24533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItem(uri, values);
24543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
24553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
24563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
24573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
24593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItemPhoto(uri, values);
24603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
24613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
24623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
24633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
24653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                values.put(StreamItemPhotos.STREAM_ITEM_ID, uri.getPathSegments().get(1));
24663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItemPhoto(uri, values);
24673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
24683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
24693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
24703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
2471a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
247281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
2473f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
2474a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2475a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
24767e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
24777e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
24787e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
24797e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2480de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        return ContentUris.withAppendedId(uri, id);
2481a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2482a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2483a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2484e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * If account is non-null then store it in the values. If the account is
2485e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * already specified in the values then it must be consistent with the
2486e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * account, if it is non-null.
2487e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *
2488e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param uri Current {@link Uri} being operated on.
2489e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param values {@link ContentValues} to read and possibly update.
2490e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when only one of
2491e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_NAME} or
2492e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_TYPE} is specified, leaving the
2493e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             other undefined.
2494e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when {@link RawContacts#ACCOUNT_NAME}
2495e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             and {@link RawContacts#ACCOUNT_TYPE} are inconsistent between
2496e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             the given {@link Uri} and {@link ContentValues}.
24977e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
2498e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey    private Account resolveAccount(Uri uri, ContentValues values) throws IllegalArgumentException {
2499f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
2500f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
2501e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
2502f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2503f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
2504f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
2505e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialValues = TextUtils.isEmpty(valueAccountName)
2506e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                ^ TextUtils.isEmpty(valueAccountType);
2507e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2508e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri || partialValues) {
2509e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
25105d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
2511fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
2512e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
2513e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2514e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
2515e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
2516e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validUri = !TextUtils.isEmpty(accountName);
2517e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validValues = !TextUtils.isEmpty(valueAccountName);
2518e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2519e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validValues && validUri) {
2520e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Check that accounts match when both present
2521e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            final boolean accountMatch = TextUtils.equals(accountName, valueAccountName)
2522e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    && TextUtils.equals(accountType, valueAccountType);
2523e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            if (!accountMatch) {
25245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
2525fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        "When both specified, ACCOUNT_NAME and ACCOUNT_TYPE must match", uri));
2526e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            }
2527e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validUri) {
2528e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Fill values from Uri when not present
2529f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_NAME, accountName);
2530f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_TYPE, accountType);
2531e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validValues) {
2532f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountName = valueAccountName;
2533f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountType = valueAccountType;
2534e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else {
2535e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            return null;
2536f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
2537f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2538e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Use cached Account object when matches, otherwise create
2539f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mAccount == null
2540f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.name.equals(accountName)
2541f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.type.equals(accountType)) {
2542f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mAccount = new Account(accountName, accountType);
2543035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
2544f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2545e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        return mAccount;
25467e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
25477e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
25487e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
254943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * Resolves the account and builds an {@link AccountWithDataSet} based on the data set specified
255043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * in the URI or values (if any).
255143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * @param uri Current {@link Uri} being operated on.
255243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * @param values {@link ContentValues} to read and possibly update.
255343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     */
255443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    private AccountWithDataSet resolveAccountWithDataSet(Uri uri, ContentValues values) {
25553593682b8d9213fde576a0cff54458ad50563980Dave Santoro        final Account account = resolveAccount(uri, values);
255643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        AccountWithDataSet accountWithDataSet = null;
255743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        if (account != null) {
255843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            String dataSet = getQueryParameter(uri, RawContacts.DATA_SET);
255943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            if (dataSet == null) {
25603593682b8d9213fde576a0cff54458ad50563980Dave Santoro                dataSet = values.getAsString(RawContacts.DATA_SET);
2561a71dc460ca951c7aca591f3f470c160cde70a1e3Dave Santoro            } else {
25623593682b8d9213fde576a0cff54458ad50563980Dave Santoro                values.put(RawContacts.DATA_SET, dataSet);
256343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
256443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            accountWithDataSet = new AccountWithDataSet(account.name, account.type, dataSet);
256543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        }
256643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        return accountWithDataSet;
256743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    }
256843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
256943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    /**
2570d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Inserts an item in the contacts table
25716bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
25726bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
25736bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
25746bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
2575d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertContact(ContentValues values) {
2576de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregate contacts are created automatically");
25776bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
25786bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
25796bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
258024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Inserts an item in the raw contacts table
2581a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2582f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param uri the values for the new row
2583f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param values the account this contact should be associated with. may be null.
2584dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana     * @param callerIsSyncAdapter
2585a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2586a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
25875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private long insertRawContact(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
2588f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2589f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2590f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2591f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
259243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        AccountWithDataSet accountWithDataSet = resolveAccountWithDataSet(uri, mValues);
25937e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
25943d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
25953d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
2596f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
25973d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
25983d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
25995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        long rawContactId = mActiveDb.get().insert(Tables.RAW_CONTACTS,
26005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                RawContacts.CONTACT_ID, mValues);
2601f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        int aggregationMode = RawContacts.AGGREGATION_MODE_DEFAULT;
26025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mValues.containsKey(RawContacts.AGGREGATION_MODE)) {
2603f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            aggregationMode = mValues.getAsInteger(RawContacts.AGGREGATION_MODE);
2604f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        }
26055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().markNewForAggregation(rawContactId, aggregationMode);
2606285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
26075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Trigger creation of a Contact based on this RawContact at the end of transaction
26085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mTransactionContext.get().rawContactInserted(rawContactId, accountWithDataSet);
2609f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2610dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter) {
2611dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            addAutoAddMembership(rawContactId);
2612dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            final Long starred = values.getAsLong(RawContacts.STARRED);
2613dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (starred != null && starred != 0) {
2614dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateFavoritesMembership(rawContactId, starred != 0);
2615dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2616dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2617dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
26183826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
2619023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        return rawContactId;
2620a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2621a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2622dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void addAutoAddMembership(long rawContactId) {
2623dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID,
2624dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2625dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2626dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            insertDataGroupMembership(rawContactId, groupId);
2627dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2628dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2629dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2630dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private Long findGroupByRawContactId(String selection, long rawContactId) {
26315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = mActiveDb.get().query(Tables.GROUPS + "," + Tables.RAW_CONTACTS,
26325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                PROJECTION_GROUP_ID, selection,
2633dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                new String[]{Long.toString(rawContactId)},
2634dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                null /* groupBy */, null /* having */, null /* orderBy */);
2635dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        try {
2636dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            while (c.moveToNext()) {
2637dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return c.getLong(0);
2638dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2639dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return null;
2640dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        } finally {
2641dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            c.close();
2642dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2643dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2644dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2645dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void updateFavoritesMembership(long rawContactId, boolean isStarred) {
2646dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID,
2647dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2648dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2649dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (isStarred) {
2650dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                insertDataGroupMembership(rawContactId, groupId);
2651dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
2652dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                deleteDataGroupMembership(rawContactId, groupId);
2653dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2654dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2655dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2656dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2657dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void insertDataGroupMembership(long rawContactId, long groupId) {
2658dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        ContentValues groupMembershipValues = new ContentValues();
2659dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.GROUP_ROW_ID, groupId);
2660dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.RAW_CONTACT_ID, rawContactId);
2661dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(DataColumns.MIMETYPE_ID,
26625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mDbHelper.get().getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
26635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.get().insert(Tables.DATA, null, groupMembershipValues);
2664dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2665dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2666dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void deleteDataGroupMembership(long rawContactId, long groupId) {
2667dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final String[] selectionArgs = {
26685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Long.toString(mDbHelper.get().getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE)),
2669dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(groupId),
2670dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(rawContactId)};
26715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.get().delete(Tables.DATA, SELECTION_GROUPMEMBERSHIP_DATA, selectionArgs);
2672dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2673dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2674a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2675a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
2676a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2677a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
2678a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2679a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2680f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
2681a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
2682de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.clear();
2683de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.putAll(values);
268467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
2685de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
268620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2687de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace package with internal mapping
2688de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
2689de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (packageName != null) {
26905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.get().getPackageId(packageName));
2691de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
2692de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.RES_PACKAGE);
2693508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2694de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace mimetype with internal mapping
2695de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String mimeType = mValues.getAsString(Data.MIMETYPE);
2696de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (TextUtils.isEmpty(mimeType)) {
2697de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
2698de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
26994097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
27005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.get().getMimeTypeId(mimeType));
2701de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
2702a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2703a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
27045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        id = rowHandler.insert(mActiveDb.get(), mTransactionContext.get(), rawContactId, mValues);
2705f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
27065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mTransactionContext.get().markRawContactDirty(rawContactId);
2707a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
27085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mTransactionContext.get().rawContactUpdated(rawContactId);
2709a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
27104f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
27114f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
27123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
27133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Inserts an item in the stream_items table.  The account is checked against the
27143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account in the raw contact for which the stream item is being inserted.  If the
27153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * new stream item results in more stream items under this raw contact than the limit,
27163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the oldest one will be deleted (note that if the stream item inserted was the
27173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * oldest, it will be immediately deleted, and this will return 0).
27183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
27193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param uri the insertion URI
27203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param values the values for the new row
27213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return the stream item _ID of the newly created row, or 0 if it was not created
27223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
27233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long insertStreamItem(Uri uri, ContentValues values) {
27243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long id = 0;
27253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.clear();
27263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.putAll(values);
27273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long rawContactId = mValues.getAsLong(StreamItems.RAW_CONTACT_ID);
27293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Ensure that the raw contact exists and belongs to the caller's account.
27313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, mValues);
27323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccount(account, rawContactId);
27333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27346802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to insert accounts params - they don't exist in the stream items table.
27356802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        mValues.remove(RawContacts.ACCOUNT_NAME);
27366802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        mValues.remove(RawContacts.ACCOUNT_TYPE);
27376802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
27383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Insert the new stream item.
27395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        id = mActiveDb.get().insert(Tables.STREAM_ITEMS, null, mValues);
27406802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (id == -1) {
27416802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Insertion failed.
27426802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return 0;
27436802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
27443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check to see if we're over the limit for stream items under this raw contact.
27463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // It's possible that the inserted stream item is older than the the existing
27473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // ones, in which case it may be deleted immediately (resetting the ID to 0).
27483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        id = cleanUpOldStreamItems(rawContactId, id);
27493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return id;
27513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
27523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
27543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Inserts an item in the stream_item_photos table.  The account is checked against
27553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the account in the raw contact that owns the stream item being modified.
27563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
27573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param uri the insertion URI
27583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param values the values for the new row
27596802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @return the stream item photo _ID of the newly created row, or 0 if there was an issue
27606802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     *     with processing the photo or creating the row
27613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
27623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long insertStreamItemPhoto(Uri uri, ContentValues values) {
27633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long id = 0;
27643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.clear();
27653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.putAll(values);
27663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long streamItemId = mValues.getAsLong(StreamItemPhotos.STREAM_ITEM_ID);
27683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        if (streamItemId != 0) {
27693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            long rawContactId = lookupRawContactIdForStreamId(streamItemId);
27703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            // Ensure that the raw contact exists and belongs to the caller's account.
27723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            Account account = resolveAccount(uri, mValues);
27733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            enforceModifyingAccount(account, rawContactId);
27743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27756802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Don't attempt to insert accounts params - they don't exist in the stream item
27766802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // photos table.
27776802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            mValues.remove(RawContacts.ACCOUNT_NAME);
27786802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            mValues.remove(RawContacts.ACCOUNT_TYPE);
27793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27806802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Process the photo and store it.
27816802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            if (processStreamItemPhoto(mValues, false)) {
27826802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // Insert the stream item photo.
27835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                id = mActiveDb.get().insert(Tables.STREAM_ITEM_PHOTOS, null, mValues);
27846802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
27853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
27863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return id;
27873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
27883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
27906802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * Processes the photo contained in the {@link ContactsContract.StreamItemPhotos#PHOTO}
27916802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * field of the given values, attempting to store it in the photo store.  If successful,
27926802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * the resulting photo file ID will be added to the values for insert/update in the table.
27936802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * <p>
27946802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * If updating, it is valid for the picture to be empty or unspecified (the function will
27956802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * still return true).  If inserting, a valid picture must be specified.
27966802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @param values The content values provided by the caller.
27976802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @param forUpdate Whether this photo is being processed for update (vs. insert).
27986802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @return Whether the insert or update should proceed.
27996802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     */
28006802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    private boolean processStreamItemPhoto(ContentValues values, boolean forUpdate) {
28016802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (!values.containsKey(StreamItemPhotos.PHOTO)) {
28026802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return forUpdate;
28036802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
28046802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        byte[] photoBytes = values.getAsByteArray(StreamItemPhotos.PHOTO);
28056802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (photoBytes == null) {
28066802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return forUpdate;
28076802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
28086802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
28096802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Process the photo and store it.
28106802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        try {
28115d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            long photoFileId = mPhotoStore.get().insert(new PhotoProcessor(photoBytes,
28121dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                    mMaxDisplayPhotoDim, mMaxThumbnailPhotoDim, true), true);
28136802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            if (photoFileId != 0) {
28146802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                values.put(StreamItemPhotos.PHOTO_FILE_ID, photoFileId);
28156802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                values.remove(StreamItemPhotos.PHOTO);
28166802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                return true;
28176802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            } else {
28186802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // Couldn't store the photo, return 0.
28196802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                Log.e(TAG, "Could not process stream item photo for insert");
28206802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                return false;
28216802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
28226802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        } catch (IOException ioe) {
28236802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            Log.e(TAG, "Could not process stream item photo for insert", ioe);
28246802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return false;
28256802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
28266802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    }
28276802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
28286802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    /**
28293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Looks up the raw contact ID that owns the specified stream item.
28303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param streamItemId The ID of the stream item.
28313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The associated raw contact ID, or -1 if no such stream item exists.
28323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
28333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long lookupRawContactIdForStreamId(long streamItemId) {
28343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long rawContactId = -1;
28355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = mActiveDb.get().query(Tables.STREAM_ITEMS,
28365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new String[]{StreamItems.RAW_CONTACT_ID},
28373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                StreamItems._ID + "=?", new String[]{String.valueOf(streamItemId)},
28383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                null, null, null);
28393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
28403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if (c.moveToFirst()) {
28413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                rawContactId = c.getLong(0);
28423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
28433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
28443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
28453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
28463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return rawContactId;
28473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
28483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
28493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
28503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given raw contact ID is owned by the given account.
28513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * If the resolved account is null, this will return true iff the raw contact
28523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * is also associated with the "null" account.
28533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
28543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * If the resolved account does not match, this will throw a security exception.
28553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
28563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param rawContactId The raw contact ID to check for.
28573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
28583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void enforceModifyingAccount(Account account, long rawContactId) {
28593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        String accountSelection = RawContactsColumns.CONCRETE_ID + "=? AND "
28603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_NAME + "=? AND "
28613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + "=?";
28623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        String noAccountSelection = RawContactsColumns.CONCRETE_ID + "=? AND "
28633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " IS NULL AND "
28643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " IS NULL";
28653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c;
28663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        if (account != null) {
28675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            c = mActiveDb.get().query(Tables.RAW_CONTACTS,
28685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    new String[]{RawContactsColumns.CONCRETE_ID}, accountSelection,
28693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    new String[]{String.valueOf(rawContactId), mAccount.name, mAccount.type},
28703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    null, null, null);
28713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } else {
28725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            c = mActiveDb.get().query(Tables.RAW_CONTACTS,
28735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    new String[]{RawContactsColumns.CONCRETE_ID}, noAccountSelection,
28745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    new String[]{String.valueOf(rawContactId)},
28753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    null, null, null);
28763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
28773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
28783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if(c.getCount() == 0) {
28793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                throw new SecurityException("Caller account does not match raw contact ID "
28803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    + rawContactId);
28813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
28823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
28833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
28843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
28853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
28863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
28873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
28883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given selection of stream items matches up with the given
28893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account.  If any of the raw contacts fail the account check, this will throw a
28903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * security exception.
28913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
28923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selection The selection.
28933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selectionArgs The selection arguments.
28943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The list of stream item IDs that would be included in this selection.
28953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
28963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private List<Long> enforceModifyingAccountForStreamItems(Account account, String selection,
28973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
28983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemIds = Lists.newArrayList();
28993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
29003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        setTablesAndProjectionMapForStreamItems(qb);
29015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = qb.query(mActiveDb.get(),
29023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{StreamItems._ID, StreamItems.RAW_CONTACT_ID},
29033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selection, selectionArgs, null, null, null);
29043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
29053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            while (c.moveToNext()) {
29063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                streamItemIds.add(c.getLong(0));
29073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Throw a security exception if the account doesn't match the raw contact's.
29093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                enforceModifyingAccount(account, c.getLong(1));
29103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
29113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
29123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
29133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
29143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemIds;
29153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
29163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
29183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given selection of stream item photos matches up with the given
29193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account.  If any of the raw contacts fail the account check, this will throw a
29203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * security exception.
29213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
29223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selection The selection.
29233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selectionArgs The selection arguments.
29243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The list of stream item photo IDs that would be included in this selection.
29253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
29263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private List<Long> enforceModifyingAccountForStreamItemPhotos(Account account, String selection,
29273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
29283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemPhotoIds = Lists.newArrayList();
29293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
29303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        setTablesAndProjectionMapForStreamItemPhotos(qb);
29315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = qb.query(mActiveDb.get(),
29325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new String[]{StreamItemPhotos._ID, StreamItems.RAW_CONTACT_ID},
29333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selection, selectionArgs, null, null, null);
29343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
29353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            while (c.moveToNext()) {
29363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                streamItemPhotoIds.add(c.getLong(0));
29373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Throw a security exception if the account doesn't match the raw contact's.
29393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                enforceModifyingAccount(account, c.getLong(1));
29403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
29413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
29423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
29433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
29443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemPhotoIds;
29453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
29463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
29483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Queries the database for stream items under the given raw contact.  If there are
29493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * more entries than {@link ContactsProvider2#MAX_STREAM_ITEMS_PER_RAW_CONTACT},
29503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the oldest entries (as determined by timestamp) will be deleted.
29513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param rawContactId The raw contact ID to examine for stream items.
29523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param insertedStreamItemId The ID of the stream item that was just inserted,
29533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     prompting this cleanup.  Callers may pass 0 if no insertion prompted the
29543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     cleanup.
29553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The ID of the inserted stream item if it still exists after cleanup;
29563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     0 otherwise.
29573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
29583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long cleanUpOldStreamItems(long rawContactId, long insertedStreamItemId) {
29593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long postCleanupInsertedStreamId = insertedStreamItemId;
29605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = mActiveDb.get().query(Tables.STREAM_ITEMS, new String[]{StreamItems._ID},
29613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                StreamItems.RAW_CONTACT_ID + "=?", new String[]{String.valueOf(rawContactId)},
29623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                null, null, StreamItems.TIMESTAMP + " DESC, " + StreamItems._ID + " DESC");
29633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
29643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            int streamItemCount = c.getCount();
29653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if (streamItemCount <= MAX_STREAM_ITEMS_PER_RAW_CONTACT) {
29663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Still under the limit - nothing to clean up!
29673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return insertedStreamItemId;
29683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            } else {
29693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                c.moveToLast();
29703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                while (c.getPosition() >= MAX_STREAM_ITEMS_PER_RAW_CONTACT) {
29713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    long streamItemId = c.getLong(0);
29723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    if (insertedStreamItemId == streamItemId) {
29733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        // The stream item just inserted is being deleted.
29743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        postCleanupInsertedStreamId = 0;
29753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    }
29763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    deleteStreamItem(c.getLong(0));
29773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    c.moveToPrevious();
29783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
29793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
29803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
29813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
29823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
29833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return postCleanupInsertedStreamId;
29843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
29853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29869261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
298720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
298820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
2989f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
299020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
299120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2992de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
2993de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
29940c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro        Uri dataUri = inProfileMode()
29950c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro                ? Uri.withAppendedPath(Profile.CONTENT_URI, RawContacts.Data.CONTENT_DIRECTORY)
29960c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro                : Data.CONTENT_URI;
29970c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro        Cursor c = query(dataUri, DataRowHandler.DataDeleteQuery.COLUMNS,
2998f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                selection, selectionArgs, null);
2999de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        try {
3000de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            while(c.moveToNext()) {
3001f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                long rawContactId = c.getLong(DataRowHandler.DataDeleteQuery.RAW_CONTACT_ID);
3002f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
3003a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                DataRowHandler rowHandler = getDataRowHandler(mimeType);
30045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                count += rowHandler.delete(mActiveDb.get(), mTransactionContext.get(), c);
3005f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                if (!callerIsSyncAdapter) {
30065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mTransactionContext.get().markRawContactDirty(rawContactId);
300788e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                }
300820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
300920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
3010de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            c.close();
301120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
301220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
301320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
301420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
301520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
301688e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    /**
301788e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     * Delete a data row provided that it is one of the allowed mime types.
301888e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     */
301920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
3020f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
302188e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
302288e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
30234da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(dataId);
3024f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataRowHandler.DataDeleteQuery.COLUMNS, Data._ID + "=?",
30254da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1, null);
3026f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
302720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
302820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
302920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
303020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
303120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3032f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov            String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
303320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
303420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
303520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
303620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
303720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
303820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
303920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
304020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
304120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
30427a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException("Data type mismatch: expected "
304320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
304420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3045a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            DataRowHandler rowHandler = getDataRowHandler(mimeType);
30465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return rowHandler.delete(mActiveDb.get(), mTransactionContext.get(), c);
304720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
304820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
304920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
305020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
305120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
305220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
3053ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
3054ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
3055f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private long insertGroup(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
3056f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
3057f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
3058f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
30593593682b8d9213fde576a0cff54458ad50563980Dave Santoro        final AccountWithDataSet accountWithDataSet = resolveAccountWithDataSet(uri, mValues);
3060ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3061ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
3062f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String packageName = mValues.getAsString(Groups.RES_PACKAGE);
306367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
30645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mValues.put(GroupsColumns.PACKAGE_ID, mDbHelper.get().getPackageId(packageName));
306567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
3066f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.remove(Groups.RES_PACKAGE);
3067ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3068dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final boolean isFavoritesGroup = mValues.getAsLong(Groups.FAVORITES) != null
3069dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                ? mValues.getAsLong(Groups.FAVORITES) != 0
3070dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                : false;
3071dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3072f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
3073f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(Groups.DIRTY, 1);
307473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
307573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
30765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        long result = mActiveDb.get().insert(Tables.GROUPS, Groups.TITLE, mValues);
3077ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
3078dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter && isFavoritesGroup) {
3079dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // add all starred raw contacts to this group
3080dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String selection;
3081dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs;
30823593682b8d9213fde576a0cff54458ad50563980Dave Santoro            if (accountWithDataSet == null) {
3083dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + " IS NULL AND "
308443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + RawContacts.ACCOUNT_TYPE + " IS NULL AND "
308543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + RawContacts.DATA_SET + " IS NULL";
3086dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = null;
30873593682b8d9213fde576a0cff54458ad50563980Dave Santoro            } else if (accountWithDataSet.getDataSet() == null) {
3088dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + "=? AND "
30893593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        + RawContacts.ACCOUNT_TYPE + "=? AND "
30903593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        + RawContacts.DATA_SET + " IS NULL";
30913593682b8d9213fde576a0cff54458ad50563980Dave Santoro                selectionArgs = new String[] {
30923593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getAccountName(),
30933593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getAccountType()
30943593682b8d9213fde576a0cff54458ad50563980Dave Santoro                };
309543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            } else {
309643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                selection = RawContacts.ACCOUNT_NAME + "=? AND "
309743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + RawContacts.ACCOUNT_TYPE + "=? AND "
309843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + RawContacts.DATA_SET + "=?";
30993593682b8d9213fde576a0cff54458ad50563980Dave Santoro                selectionArgs = new String[] {
31003593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getAccountName(),
31013593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getAccountType(),
31023593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getDataSet()
31033593682b8d9213fde576a0cff54458ad50563980Dave Santoro                };
3104dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
31055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS,
3106dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[]{RawContacts._ID, RawContacts.STARRED},
3107dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    selection, selectionArgs, null, null, null);
3108892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            try {
3109892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                while (c.moveToNext()) {
3110892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (c.getLong(1) != 0) {
3111892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        final long rawContactId = c.getLong(0);
3112892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        insertDataGroupMembership(rawContactId, result);
31135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mTransactionContext.get().markRawContactDirty(rawContactId);
3114892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    }
3115dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
3116892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            } finally {
3117892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                c.close();
3118dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
3119dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
3120dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3121f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mValues.containsKey(Groups.GROUP_VISIBLE)) {
31221a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3123ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        }
3124ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
3125ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        return result;
3126ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
3127ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
31285aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private long insertSettings(Uri uri, ContentValues values) {
3129f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        // Before inserting, ensure that no settings record already exists for the
3130f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        // values being inserted (this used to be enforced by a primary key, but that no
3131f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        // longer works with the nullable data_set field added).
3132f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        String accountName = values.getAsString(Settings.ACCOUNT_NAME);
3133f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        String accountType = values.getAsString(Settings.ACCOUNT_TYPE);
3134f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        String dataSet = values.getAsString(Settings.DATA_SET);
3135f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        Uri.Builder settingsUri = Settings.CONTENT_URI.buildUpon();
3136f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        if (accountName != null) {
3137f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            settingsUri.appendQueryParameter(Settings.ACCOUNT_NAME, accountName);
3138f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        }
3139f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        if (accountType != null) {
3140f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            settingsUri.appendQueryParameter(Settings.ACCOUNT_TYPE, accountType);
3141f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        }
3142f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        if (dataSet != null) {
3143f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            settingsUri.appendQueryParameter(Settings.DATA_SET, dataSet);
3144f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        }
3145f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        Cursor c = queryLocal(settingsUri.build(), null, null, null, null, 0);
3146f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        try {
3147f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            if (c.getCount() > 0) {
3148f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                throw new SQLiteConstraintException("Can't insert a settings record with the same "
3149f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                        + "account name/type/data set");
3150f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            }
3151f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        } finally {
3152f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            c.close();
3153f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        }
3154f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro
3155f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        // If we didn't find a duplicate, we're fine to insert.
31565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        final long id = mActiveDb.get().insert(Tables.SETTINGS, null, values);
31575aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
31581a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
31591a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3160e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
31611a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
3162e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return id;
3163e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3164e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3165ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
316682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov     * Inserts a status update.
31671f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
316882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    public long insertStatusUpdate(ContentValues values) {
316982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        final String handle = values.getAsString(StatusUpdates.IM_HANDLE);
31700a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        final Integer protocol = values.getAsInteger(StatusUpdates.PROTOCOL);
31714dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        String customProtocol = null;
31724dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
31730a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        if (protocol != null && protocol == Im.PROTOCOL_CUSTOM) {
317482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            customProtocol = values.getAsString(StatusUpdates.CUSTOM_PROTOCOL);
31754dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (TextUtils.isEmpty(customProtocol)) {
31764dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                throw new IllegalArgumentException(
31774dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                        "CUSTOM_PROTOCOL is required when PROTOCOL=PROTOCOL_CUSTOM");
31784dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
31791f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
31801f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3181dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long rawContactId = -1;
3182dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long contactId = -1;
318382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Long dataId = values.getAsLong(StatusUpdates.DATA_ID);
31846802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        String accountType = null;
31856802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        String accountName = null;
3186f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.setLength(0);
31872526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov        mSelectionArgs.clear();
3188dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        if (dataId != null) {
3189dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the contact info for the given data row.
3190dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
31912526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSb.append(Tables.DATA + "." + Data._ID + "=?");
31922526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSelectionArgs.add(String.valueOf(dataId));
31931f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
3194dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the data row to attach this presence update to
3195dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
31960a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(handle) || protocol == null) {
31970a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                throw new IllegalArgumentException("PROTOCOL and IM_HANDLE are required");
31980a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
31990a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
3200dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // TODO: generalize to allow other providers to match against email
3201dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == protocol;
3202dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
32035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            String mimeTypeIdIm = String.valueOf(mDbHelper.get().getMimeTypeIdForIm());
3204dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            if (matchEmail) {
32055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                String mimeTypeIdEmail = String.valueOf(mDbHelper.get().getMimeTypeIdForEmail());
3206f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
3207f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // The following hack forces SQLite to use the (mimetype_id,data1) index, otherwise
3208f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the "OR" conjunction confuses it and it switches to a full scan of
3209f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the raw_contacts table.
3210f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
3211f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // This code relies on the fact that Im.DATA and Email.DATA are in fact the same
3212f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // column - Data.DATA1
32132526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + " IN (?,?)" +
32142526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Data.DATA1 + "=?" +
32152526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND ((" + DataColumns.MIMETYPE_ID + "=? AND " + Im.PROTOCOL + "=?");
32162526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
32172526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
32182526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
32192526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
32202526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
3221dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
32222526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
32232526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
3224dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
32252526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(") OR (" + DataColumns.MIMETYPE_ID + "=?))");
32262526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
3227dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            } else {
32282526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + "=?" +
32292526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.PROTOCOL + "=?" +
32302526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.DATA + "=?");
32312526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
32322526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
32332526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
3234dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
32352526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
32362526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
3237dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
3238dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
32391f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
324082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.DATA_ID)) {
32412526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(" AND " + DataColumns.CONCRETE_ID + "=?");
32422526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(values.getAsString(StatusUpdates.DATA_ID));
3243dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
324470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
324570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
32461f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
32471f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
32485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            cursor = mActiveDb.get().query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
32492526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.toString(), mSelectionArgs.toArray(EMPTY_STRING_ARRAY), null, null,
32504394086494fe7909aaca70f56fb4bb08beebf303Dmitri Plotnikov                    Clauses.CONTACT_VISIBLE + " DESC, " + Data.RAW_CONTACT_ID);
32511f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
325267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
32535ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
32546802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                accountType = cursor.getString(DataContactsQuery.ACCOUNT_TYPE);
32556802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                accountName = cursor.getString(DataContactsQuery.ACCOUNT_NAME);
3256e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
32571f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
32581f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
32591f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
32601f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
32611f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
326231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
326331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
326431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
32651f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
32661f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
326782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.PRESENCE)) {
3268a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (customProtocol == null) {
3269a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // We cannot allow a null in the custom protocol field, because SQLite3 does not
3270a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // properly enforce uniqueness of null values
3271a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                customProtocol = "";
3272a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
3273a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3274a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.clear();
327582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.DATA_ID, dataId);
3276a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.RAW_CONTACT_ID, rawContactId);
3277a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.CONTACT_ID, contactId);
327882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PROTOCOL, protocol);
327982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
328082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.IM_HANDLE, handle);
328182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.IM_ACCOUNT)) {
328282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                mValues.put(StatusUpdates.IM_ACCOUNT, values.getAsString(StatusUpdates.IM_ACCOUNT));
3283a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
328482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PRESENCE,
328582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    values.getAsString(StatusUpdates.PRESENCE));
3286aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori            mValues.put(StatusUpdates.CHAT_CAPABILITY,
3287aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                    values.getAsString(StatusUpdates.CHAT_CAPABILITY));
32881f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3289a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            // Insert the presence update
32905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().replace(Tables.PRESENCE, null, mValues);
3291a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3292e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
32930a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
329482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.STATUS)) {
329582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String status = values.getAsString(StatusUpdates.STATUS);
32960a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String resPackage = values.getAsString(StatusUpdates.STATUS_RES_PACKAGE);
32970bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            Resources resources = getContext().getResources();
32980bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            if (!TextUtils.isEmpty(resPackage)) {
32990bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                PackageManager pm = getContext().getPackageManager();
33000bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                try {
33010bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                    resources = pm.getResourcesForApplication(resPackage);
33020bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                } catch (NameNotFoundException e) {
33030bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                    Log.w(TAG, "Contact status update resource package not found: "
33040bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                            + resPackage);
33050bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                }
33060bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            }
33070bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            Integer labelResourceId = values.getAsInteger(StatusUpdates.STATUS_LABEL);
33080a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
33090bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            if ((labelResourceId == null || labelResourceId == 0) && protocol != null) {
33100bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                labelResourceId = Im.getProtocolLabelResource(protocol);
33110a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
33120bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            String labelResource = getResourceName(resources, "string", labelResourceId);
33130a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
33140bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            Integer iconResourceId = values.getAsInteger(StatusUpdates.STATUS_ICON);
33150a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            // TODO compute the default icon based on the protocol
33160a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
33170bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            String iconResource = getResourceName(resources, "drawable", iconResourceId);
33180bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann
3319a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (TextUtils.isEmpty(status)) {
33205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mDbHelper.get().deleteStatusUpdate(dataId);
3321a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            } else {
33226802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                Long timestamp = values.getAsLong(StatusUpdates.STATUS_TIMESTAMP);
33236802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (timestamp != null) {
33245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mDbHelper.get().replaceStatusUpdate(dataId, timestamp, status, resPackage,
33250bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                            iconResourceId, labelResourceId);
33266802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                } else {
33275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mDbHelper.get().insertStatusUpdate(dataId, status, resPackage, iconResourceId,
33280bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                            labelResourceId);
33296802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                }
33306802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
33316802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // For forward compatibility with the new stream item API, insert this status update
33326802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // there as well.  If we already have a stream item from this source, update that
33336802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // one instead of inserting a new one (since the semantics of the old status update
33346802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // API is to only have a single record).
33356802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (rawContactId != -1 && !TextUtils.isEmpty(status)) {
33366802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    ContentValues streamItemValues = new ContentValues();
33376802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RAW_CONTACT_ID, rawContactId);
3338d5ef5903570e533a501abe6a8e3d533fdb5318fcFlavio Lerda                    // Status updates are text only but stream items are HTML.
3339e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda                    streamItemValues.put(StreamItems.TEXT, statusUpdateToHtml(status));
33406802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.COMMENTS, "");
33416802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_PACKAGE, resPackage);
33426802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_ICON, iconResource);
33436802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_LABEL, labelResource);
33446802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.TIMESTAMP,
33456802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            timestamp == null ? System.currentTimeMillis() : timestamp);
33466802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
33476802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // Note: The following is basically a workaround for the fact that status
33486802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // updates didn't do any sort of account enforcement, while social stream item
33496802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // updates do.  We can't expect callers of the old API to start passing account
33506802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // information along, so we just populate the account params appropriately for
335143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    // the raw contact.  Data set is not relevant here, as we only check account
335243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    // name and type.
33536802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    if (accountName != null && accountType != null) {
33546802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        streamItemValues.put(RawContacts.ACCOUNT_NAME, accountName);
33556802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        streamItemValues.put(RawContacts.ACCOUNT_TYPE, accountType);
33566802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    }
33576802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
33586802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // Check for an existing stream item from this source, and insert or update.
33596802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    Uri streamUri = StreamItems.CONTENT_URI;
33606802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    Cursor c = query(streamUri, new String[]{StreamItems._ID},
33616802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            StreamItems.RAW_CONTACT_ID + "=?",
33626802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            new String[]{String.valueOf(rawContactId)}, null);
33636802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    try {
33646802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        if (c.getCount() > 0) {
33656802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            c.moveToFirst();
33666802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            update(ContentUris.withAppendedId(streamUri, c.getLong(0)),
33676802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    streamItemValues, null, null);
33686802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        } else {
33696802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            insert(streamUri, streamItemValues);
33706802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        }
33716802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    } finally {
33726802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        c.close();
33736802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    }
33746802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                }
3375e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            }
3376e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        }
3377bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
3378a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (contactId != -1) {
33795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mAggregator.get().updateLastStatusUpdateId(contactId);
3380a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3381a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3382a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        return dataId;
33831f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
33841f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3385e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda    /** Converts a status update to HTML. */
3386e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda    private String statusUpdateToHtml(String status) {
33874747809486541f7a3d342d3e1dd48fb5ea255ad6Flavio Lerda        return TextUtils.htmlEncode(status);
3388e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda    }
3389e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda
33900bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann    private String getResourceName(Resources resources, String expectedType, Integer resourceId) {
33910bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann        try {
33920bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            if (resourceId == null || resourceId == 0) return null;
33930bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann
33940bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            // Resource has an invalid type (e.g. a string as icon)? ignore
33950bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            final String resourceEntryName = resources.getResourceEntryName(resourceId);
33960bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            final String resourceTypeName = resources.getResourceTypeName(resourceId);
33970bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            if (!expectedType.equals(resourceTypeName)) {
33980bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                Log.w(TAG, "Resource " + resourceId + " (" + resourceEntryName + ") is of type " +
33990bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                        resourceTypeName + " but " + expectedType + " is required.");
34000bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                return null;
34010bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            }
34020bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann
34030bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            return resourceEntryName;
34040bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann        } catch (NotFoundException e) {
34050bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            return null;
34060bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann        }
34070bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann    }
34080bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann
34094f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3410de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
3411bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3412b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "deleteInTransaction: " + uri);
3413b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
34145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
34155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
34165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
34175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.set(mDb);
34185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
34195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
3420b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3421f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3422f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
3423508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
3424508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
342535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
34265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE:
34275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().delete(mActiveDb.get(), selection,
34285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionArgs);
34295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
34305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case SYNCSTATE_ID: {
34315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                String selectionWithId =
34325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
34335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        + (selection == null ? "" : " AND (" + selection + ")");
34345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().delete(mActiveDb.get(), selectionWithId,
34355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionArgs);
34365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
343735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
34385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE_ID: {
3439b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3440b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3441b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
34425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mProfileHelper.getSyncState().delete(mActiveDb.get(), selectionWithId,
34435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionArgs);
34445d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
3445b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3446cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            case CONTACTS: {
3447cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // TODO
3448cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return 0;
3449cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3450cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3451d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
3452d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3453dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
34546bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
34556bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
34569fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP: {
34572e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
34582e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
34592e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
34605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
3461fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
34622e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
34632e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
34645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                final long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
3465dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
34662e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
34672e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
34689fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP_ID: {
34699fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                // lookup contact by id and lookup key to see if they still match the actual record
34709fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final List<String> pathSegments = uri.getPathSegments();
34719fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final String lookupKey = pathSegments.get(2);
34729fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
34739fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                setTablesAndProjectionMapForContacts(lookupQb, uri, null);
3474a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
34759fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                String[] args;
34769fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                if (selectionArgs == null) {
34779fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[2];
34789fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } else {
34799fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[selectionArgs.length + 2];
34809fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
34819fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
34829fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                args[0] = String.valueOf(contactId);
348360de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann                args[1] = Uri.encode(lookupKey);
34849fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                lookupQb.appendWhere(Contacts._ID + "=? AND " + Contacts.LOOKUP_KEY + "=?");
34855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = query(mActiveDb.get(), lookupQb, null, selection, args, null, null,
34865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        null);
34879fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                try {
34889fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    if (c.getCount() == 1) {
34899fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // contact was unmodified so go ahead and delete it
3490dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        return deleteContact(contactId, callerIsSyncAdapter);
34919fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    } else {
34929fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // row was changed (e.g. the merging might have changed), we got multiple
34939fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // rows or the supplied selection filtered the record out
34949fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        return 0;
34959fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    }
34969fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } finally {
34979fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    c.close();
34989fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
34999fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            }
35009fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
3501d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS:
3502d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS: {
35032971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
35045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS,
3505fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        new String[]{RawContacts._ID, RawContacts.CONTACT_ID},
3506e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
35072971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
35082971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
35092971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                        final long rawContactId = c.getLong(0);
3510fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        long contactId = c.getLong(1);
3511fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        numDeletes += deleteRawContact(rawContactId, contactId,
3512fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                                callerIsSyncAdapter);
35132971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
35142971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
35152971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
35162971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
35172971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
35182971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
35192971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
3520d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS_ID:
3521d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID: {
35222971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                final long rawContactId = ContentUris.parseId(uri);
35235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return deleteRawContact(rawContactId, mDbHelper.get().getContactId(rawContactId),
3524fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        callerIsSyncAdapter);
3525508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3526508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
35270c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case DATA:
35280c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case PROFILE_DATA: {
3529f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
3530944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                return deleteData(appendAccountToSelection(uri, selection), selectionArgs,
3531f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        callerIsSyncAdapter);
353220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
353320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
353448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
353548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
353648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
3537d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case POSTALS_ID:
3538d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_DATA_ID: {
3539508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
3540f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
35414da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
35424da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                return deleteData(Data._ID + "=?", mSelectionArgs1, callerIsSyncAdapter);
3543ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3544ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3545ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3546f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
35475aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteGroup(uri, ContentUris.parseId(uri), callerIsSyncAdapter);
35482971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
35492971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
35502971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case GROUPS: {
35512971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
35525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = mActiveDb.get().query(Tables.GROUPS, new String[]{Groups._ID},
3553e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
35542971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
35552971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
35565aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        numDeletes += deleteGroup(uri, c.getLong(0), callerIsSyncAdapter);
35572971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
35582971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
35592971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
35602971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
356181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (numDeletes > 0) {
3562f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
356381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
35642971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
3565508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3566508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
3567eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
356843880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3569e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                return deleteSettings(uri, appendAccountToSelection(uri, selection), selectionArgs);
3570eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3571eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
35725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case STATUS_UPDATES:
35735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_STATUS_UPDATES: {
35740a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                return deleteStatusUpdates(selection, selectionArgs);
35751f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
35761f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
35773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
35783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
35793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItems(uri, new ContentValues(), selection, selectionArgs);
35803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
35813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
35823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
35833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
35843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItems(uri, new ContentValues(),
35859b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann                        StreamItems._ID + "=?",
35863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{uri.getLastPathSegment()});
35873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
35883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
358982780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            case RAW_CONTACTS_ID_STREAM_ITEMS_ID: {
359082780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                mSyncToNetwork |= !callerIsSyncAdapter;
359182780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                String rawContactId = uri.getPathSegments().get(1);
359282780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                String streamItemId = uri.getLastPathSegment();
359382780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                return deleteStreamItems(uri, new ContentValues(),
359482780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        StreamItems.RAW_CONTACT_ID + "=? AND " + StreamItems._ID + "=?",
359582780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        new String[]{rawContactId, streamItemId});
359682780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
359782780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            }
359882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
35993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
36003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
36015d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                String streamItemId = uri.getPathSegments().get(1);
36025d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                String selectionWithId =
36035d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                        (StreamItemPhotos.STREAM_ITEM_ID + "=" + streamItemId + " ")
36045d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                                + (selection == null ? "" : " AND (" + selection + ")");
36055d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                return deleteStreamItemPhotos(uri, new ContentValues(),
36065d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                        selectionWithId, selectionArgs);
36073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
36083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
36093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
36103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
36113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
36123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
36133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItemPhotos(uri, new ContentValues(),
36143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=? AND "
36153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                                + StreamItemPhotos.STREAM_ITEM_ID + "=?",
36163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{streamItemPhotoId, streamItemId});
36173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
36183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
361981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
362081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
36213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
362281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
3623508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
36244f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
36254f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
36261c8e40c18f92722b9bec6e8ce2e345a9828efa16Dmitri Plotnikov    public int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) {
3627ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
36285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        final long groupMembershipMimetypeId = mDbHelper.get()
362994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
36305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.get().delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
363194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
363294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupId, null);
363394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
363494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        try {
3635f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (callerIsSyncAdapter) {
36365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mActiveDb.get().delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
363794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            } else {
363894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.clear();
363994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.put(Groups.DELETED, 1);
3640f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mValues.put(Groups.DIRTY, 1);
36415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mActiveDb.get().update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId,
36425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        null);
364394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            }
364494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        } finally {
36451a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
364694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
364794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
364894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
36495aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
36505d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        final int count = mActiveDb.get().delete(Tables.SETTINGS, selection, selectionArgs);
36511a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        mVisibleTouched = true;
3652e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3653e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3654e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3655dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int deleteContact(long contactId, boolean callerIsSyncAdapter) {
365696b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(contactId);
36575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
365896b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                RawContacts.CONTACT_ID + "=?", mSelectionArgs1,
365996b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                null, null, null);
3660cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        try {
3661cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            while (c.moveToNext()) {
3662cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                long rawContactId = c.getLong(0);
3663dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
3664cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3665cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } finally {
3666cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            c.close();
3667cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
3668cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
36693826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
36703826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
36715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
3672cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3673cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3674fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov    public int deleteRawContact(long rawContactId, long contactId, boolean callerIsSyncAdapter) {
36755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().invalidateAggregationExceptionCache();
36763826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
36773826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
367882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        // Find and delete stream items associated with the raw contact.
367982780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        Cursor c = mActiveDb.get().query(Tables.STREAM_ITEMS,
368082780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                new String[]{StreamItems._ID},
368182780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                StreamItems.RAW_CONTACT_ID + "=?", new String[]{String.valueOf(rawContactId)},
368282780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                null, null, null);
368382780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        try {
368482780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            while (c.moveToNext()) {
368582780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                deleteStreamItem(c.getLong(0));
368682780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            }
368782780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        } finally {
368882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            c.close();
368982780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        }
369082780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
3691d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro        if (callerIsSyncAdapter || rawContactIsLocal(rawContactId)) {
36925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().delete(Tables.PRESENCE,
36935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
36945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            int count = mActiveDb.get().delete(Tables.RAW_CONTACTS,
36955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    RawContacts._ID + "=" + rawContactId, null);
36965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mAggregator.get().updateDisplayNameForContact(mActiveDb.get(), contactId);
3697fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return count;
369833b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } else {
36995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mDbHelper.get().removeContactIfSingleton(rawContactId);
3700dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
370133b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        }
370233b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
370333b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
3704d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro    /**
3705d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro     * Returns whether the given raw contact ID is local (i.e. has no account associated with it).
3706d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro     */
3707d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro    private boolean rawContactIsLocal(long rawContactId) {
3708d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro        Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS,
3709d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                new String[] {
3710d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                        RawContacts.ACCOUNT_NAME,
3711d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                        RawContacts.ACCOUNT_TYPE,
3712d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                        RawContacts.DATA_SET
3713d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                },
3714d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                RawContacts._ID + "=?",
3715d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                new String[] {String.valueOf(rawContactId)}, null, null, null);
3716d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro        try {
3717d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            return c.moveToFirst() && c.isNull(0) && c.isNull(1) && c.isNull(2);
3718d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro        } finally {
3719d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            c.close();
3720d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro        }
3721d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro    }
3722d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro
37230a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private int deleteStatusUpdates(String selection, String[] selectionArgs) {
37249705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // delete from both tables: presence and status_updates
37259705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // TODO should account type/name be appended to the where clause?
37269705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      if (VERBOSE_LOGGING) {
37279705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          Log.v(TAG, "deleting data from status_updates for " + selection);
37289705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      }
37295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro      mActiveDb.get().delete(Tables.STATUS_UPDATES, getWhereClauseForStatusUpdatesTable(selection),
37309705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          selectionArgs);
37315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro      return mActiveDb.get().delete(Tables.PRESENCE, selection, selectionArgs);
37320a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
37330a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
37343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItems(Uri uri, ContentValues values, String selection,
37353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
37363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // First query for the stream items to be deleted, and check that they belong
37373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // to the account.
37383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
37393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemIds = enforceModifyingAccountForStreamItems(
37403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                account, selection, selectionArgs);
37413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
37423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If no security exception has been thrown, we're fine to delete.
37433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        for (long streamItemId : streamItemIds) {
37443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            deleteStreamItem(streamItemId);
37453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
37463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
37473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mVisibleTouched = true;
37483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemIds.size();
37493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
37503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
37513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItem(long streamItemId) {
37523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Note that this does not enforce the modifying account.
37533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        deleteStreamItemPhotos(streamItemId);
37545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().delete(Tables.STREAM_ITEMS, StreamItems._ID + "=?",
37553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{String.valueOf(streamItemId)});
37563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
37573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
37583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItemPhotos(Uri uri, ContentValues values, String selection,
37593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
37603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // First query for the stream item photos to be deleted, and check that they
37613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // belong to the account.
37623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
37633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItemPhotos(account, selection, selectionArgs);
37643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
37653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If no security exception has been thrown, we're fine to delete.
37665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().delete(Tables.STREAM_ITEM_PHOTOS, selection, selectionArgs);
37673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
37683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
37693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItemPhotos(long streamItemId) {
37703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Note that this does not enforce the modifying account.
37715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().delete(Tables.STREAM_ITEM_PHOTOS,
37725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                StreamItemPhotos.STREAM_ITEM_ID + "=?",
37733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{String.valueOf(streamItemId)});
37743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
37753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
3776dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int markRawContactAsDeleted(long rawContactId, boolean callerIsSyncAdapter) {
377781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = true;
377881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
3779cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.clear();
3780cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DELETED, 1);
3781cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
3782cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
3783cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
3784cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DIRTY, 1);
3785dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        return updateRawContact(rawContactId, mValues, callerIsSyncAdapter);
3786cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3787cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
37884f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3789de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
3790de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            String[] selectionArgs) {
3791bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3792b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "updateInTransaction: " + uri);
3793b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3794b5a4add17815167d20a90645779df34cdf45280dFred Quintana
37955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
37965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
37975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.set(mDb);
37985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
37995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
380035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
380100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
380200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
3803b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (match == SYNCSTATE_ID && selection == null) {
3804b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long rowId = ContentUris.parseId(uri);
38051129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Object data = values.get(ContactsContract.SyncState.DATA);
38065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mTransactionContext.get().syncStateUpdated(rowId, data);
3807b5a4add17815167d20a90645779df34cdf45280dFred Quintana            return 1;
3808b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3809b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3810f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3811f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
381200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
381335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
38145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE:
38155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().update(mActiveDb.get(), values,
3816b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        appendAccountToSelection(uri, selection), selectionArgs);
3817b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3818b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID: {
3819b5a4add17815167d20a90645779df34cdf45280dFred Quintana                selection = appendAccountToSelection(uri, selection);
3820b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3821b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3822b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
38235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().update(mActiveDb.get(), values,
38245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionWithId, selectionArgs);
38255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
38265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
38275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE_ID: {
38285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                selection = appendAccountToSelection(uri, selection);
38295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                String selectionWithId =
38305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
38315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        + (selection == null ? "" : " AND (" + selection + ")");
38325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mProfileHelper.getSyncState().update(mActiveDb.get(), values,
3833b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        selectionWithId, selectionArgs);
3834b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
383535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3836d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case CONTACTS:
3837d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE: {
3838dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(values, selection, selectionArgs, callerIsSyncAdapter);
383900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
384000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
384100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3842d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
3843dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(ContentUris.parseId(uri), values, callerIsSyncAdapter);
3844c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
3845c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
3846c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
38472e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
38482e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
38492e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
38502e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
38512e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
38525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
3853fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
38542e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
38552e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
38565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                final long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
3857dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(contactId, values, callerIsSyncAdapter);
38582e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                break;
38592e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
38602e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
3861d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS_DATA:
3862d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID_DATA: {
3863d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                int segment = match == RAW_CONTACTS_DATA ? 1 : 2;
3864d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                final String rawContactId = uri.getPathSegments().get(segment);
38657d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
38667d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                    + (selection == null ? "" : " AND " + selection);
38677d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
38687d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter);
38697d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
38707d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                break;
38717d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            }
38727d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
38730c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case DATA:
38740c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case PROFILE_DATA: {
3875944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                count = updateData(uri, values, appendAccountToSelection(uri, selection),
3876f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
387781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3878f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
387981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
388020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
388120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3882c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
388348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
388448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
388548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
388648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
3887f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
388881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3889f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
389081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
389100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
389200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
38937e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
38945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case RAW_CONTACTS:
38955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_RAW_CONTACTS: {
38965ac5c70d1165309302ebcc931f51723e37d31e0bJeff Sharkey                selection = appendAccountToSelection(uri, selection);
3897dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateRawContacts(values, selection, selectionArgs, callerIsSyncAdapter);
38987e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
38997e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
39007e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
39015ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
390233b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
39034529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                if (selection != null) {
39044da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
39054da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=?"
3906dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                                    + " AND(" + selection + ")", selectionArgs,
3907dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
39084529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                } else {
39094da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(rawContactId);
3910dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    count = updateRawContacts(values, RawContacts._ID + "=?", mSelectionArgs1,
3911dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
39124529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                }
39137e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
39147e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
39157e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
3916ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
39175aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, appendAccountToSelection(uri, selection),
3918f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
391981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3920f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
392181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3922ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3923ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3924ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3925ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3926ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
39274da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(groupId));
39284da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                String selectionWithId = Groups._ID + "=? "
392973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                        + (selection == null ? "" : " AND " + selection);
39305aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, selectionWithId, selectionArgs,
39315aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        callerIsSyncAdapter);
393281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3933f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
393481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3935ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3936ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3937ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3938127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
39395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                count = updateAggregationException(mActiveDb.get(), values);
3940b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
3941b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
3942b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
3943eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
3944e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                count = updateSettings(uri, values, appendAccountToSelection(uri, selection),
3945e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        selectionArgs);
394643880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3947eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
3948eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3949eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
39505d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case STATUS_UPDATES:
39515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_STATUS_UPDATES: {
39529705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                count = updateStatusUpdate(uri, values, selection, selectionArgs);
39539705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                break;
39549705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            }
39559705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
39563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
39573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItems(uri, values, selection, selectionArgs);
39583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
39593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
39603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
39613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
39629b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann                count = updateStreamItems(uri, values, StreamItems._ID + "=?",
39633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{uri.getLastPathSegment()});
39643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
39653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
39663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
396782780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            case RAW_CONTACTS_ID_STREAM_ITEMS_ID: {
396882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                String rawContactId = uri.getPathSegments().get(1);
396982780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                String streamItemId = uri.getLastPathSegment();
397082780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                count = updateStreamItems(uri, values,
397182780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        StreamItems.RAW_CONTACT_ID + "=? AND " + StreamItems._ID + "=?",
397282780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        new String[]{rawContactId, streamItemId});
397382780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                break;
397482780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            }
397582780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
39763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
39773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values, selection, selectionArgs);
39783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
39793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
39803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
39813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
39823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
39833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values,
39843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotos.STREAM_ITEM_ID + "=?", new String[]{streamItemId});
39853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
39863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
39873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
39883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
39893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
39903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
39913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values,
39923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=? AND " +
39933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                                StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=?",
39943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{streamItemPhotoId, streamItemId});
39953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
39963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
39973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
399872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            case DIRECTORIES: {
3999bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                mContactDirectoryManager.scanPackagesByUid(Binder.getCallingUid());
400072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                count = 1;
4001d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
4002d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4003d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
400446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            case DATA_USAGE_FEEDBACK_ID: {
400546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (handleDataUsageFeedback(uri)) {
400646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    count = 1;
400746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                } else {
400846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    count = 0;
400946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
401046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                break;
401146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
401246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
401381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
401481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
4015f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
401681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
401700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
401800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
401900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
40204f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
40214f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
40229705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private int updateStatusUpdate(Uri uri, ContentValues values, String selection,
40239705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        String[] selectionArgs) {
40249705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // update status_updates table, if status is provided
40259705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO should account type/name be appended to the where clause?
40269705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        int updateCount = 0;
40279705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContentValues settableValues = getSettableColumnsForStatusUpdatesTable(values);
40289705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
40295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro          updateCount = mActiveDb.get().update(Tables.STATUS_UPDATES,
40309705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    settableValues,
40319705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    getWhereClauseForStatusUpdatesTable(selection),
40329705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selectionArgs);
40339705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
40349705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
40359705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // now update the Presence table
40369705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        settableValues = getSettableColumnsForPresenceTable(values);
40379705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
40385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro          updateCount = mActiveDb.get().update(Tables.PRESENCE, settableValues,
40399705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selection, selectionArgs);
40409705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
40419705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO updateCount is not entirely a valid count of updated rows because 2 tables could
40429705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // potentially get updated in this method.
40439705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return updateCount;
40449705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
40459705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
40463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int updateStreamItems(Uri uri, ContentValues values, String selection,
40473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
40483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Stream items can't be moved to a new raw contact.
40493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        values.remove(StreamItems.RAW_CONTACT_ID);
40503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
40513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check that the stream items being updated belong to the account.
40523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
40533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItems(account, selection, selectionArgs);
40543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
40556802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to update accounts params - they don't exist in the stream items table.
40566802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_NAME);
40576802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_TYPE);
40586802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
40593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If there's been no exception, the update should be fine.
40605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().update(Tables.STREAM_ITEMS, values, selection, selectionArgs);
40613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
40623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
40633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int updateStreamItemPhotos(Uri uri, ContentValues values, String selection,
40643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
40653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Stream item photos can't be moved to a new stream item.
40663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        values.remove(StreamItemPhotos.STREAM_ITEM_ID);
40673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
40683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check that the stream item photos being updated belong to the account.
40693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
40703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItemPhotos(account, selection, selectionArgs);
40713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
40726802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to update accounts params - they don't exist in the stream item
40736802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // photos table.
40746802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_NAME);
40756802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_TYPE);
40766802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
40776802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Process the photo (since we're updating, it's valid for the photo to not be present).
40786802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (processStreamItemPhoto(values, true)) {
40796802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // If there's been no exception, the update should be fine.
40805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return mActiveDb.get().update(Tables.STREAM_ITEM_PHOTOS, values, selection,
40815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    selectionArgs);
40826802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
40836802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        return 0;
40843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
40853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
40869705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    /**
40879705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     * Build a where clause to select the rows to be updated in status_updates table.
40889705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     */
40899705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private String getWhereClauseForStatusUpdatesTable(String selection) {
40909705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.setLength(0);
40919705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE);
40929705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(selection);
40939705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(")");
40949705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mSb.toString();
40959705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
40969705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
40979705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForStatusUpdatesTable(ContentValues values) {
40989705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
40999705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS, values,
41009705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS);
41019705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_TIMESTAMP, values,
41029705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_TIMESTAMP);
41039705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_RES_PACKAGE, values,
41049705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_RES_PACKAGE);
41059705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_LABEL, values,
41069705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_LABEL);
41079705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_ICON, values,
41089705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_ICON);
41099705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
41109705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
41119705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
41129705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForPresenceTable(ContentValues values) {
41139705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
41149705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.PRESENCE, values,
41159705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.PRESENCE);
4116aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.CHAT_CAPABILITY, values,
4117aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                StatusUpdates.CHAT_CAPABILITY);
41189705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
41199705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
41209705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
41215aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int updateGroups(Uri uri, ContentValues values, String selectionWithId,
4122f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
412373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
4124ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
4125ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
412673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        ContentValues updatedValues;
4127f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter && !values.containsKey(Groups.DIRTY)) {
412873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = mValues;
412973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.clear();
413073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.putAll(values);
413173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.put(Groups.DIRTY, 1);
413273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } else {
413373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = values;
413473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
413573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
41365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int count = mActiveDb.get().update(Tables.GROUPS, updatedValues, selectionWithId,
41375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                selectionArgs);
41381a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) {
41391a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
414094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
414143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
414243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        // TODO: This will not work for groups that have a data set specified, since the content
414343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        // resolver will not be able to request a sync for the right source (unless it is updated
414443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        // to key off account with data set).
41456ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        if (updatedValues.containsKey(Groups.SHOULD_SYNC)
41461129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                && updatedValues.getAsInteger(Groups.SHOULD_SYNC) != 0) {
41475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            Cursor c = mActiveDb.get().query(Tables.GROUPS, new String[]{Groups.ACCOUNT_NAME,
4148e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    Groups.ACCOUNT_TYPE}, selectionWithId, selectionArgs, null,
41496ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    null, null);
41506ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountName;
41516ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountType;
41526ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            try {
41536ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                while (c.moveToNext()) {
41546ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountName = c.getString(0);
41556ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountType = c.getString(1);
415624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
41576ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        Account account = new Account(accountName, accountType);
4158ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        ContentResolver.requestSync(account, ContactsContract.AUTHORITY,
41596ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                                new Bundle());
41606ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        break;
41616ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    }
41626ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                }
41636ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            } finally {
41646ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                c.close();
41656ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            }
41666ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        }
416794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        return count;
416894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
416994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
4170b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private int updateSettings(Uri uri, ContentValues values, String selection,
4171b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            String[] selectionArgs) {
41725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        final int count = mActiveDb.get().update(Tables.SETTINGS, values, selection, selectionArgs);
41731a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
41741a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
4175e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
4176e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
4177e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
4178e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
4179dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContacts(ContentValues values, String selection, String[] selectionArgs,
4180dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
41814529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (values.containsKey(RawContacts.CONTACT_ID)) {
41824529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            throw new IllegalArgumentException(RawContacts.CONTACT_ID + " should not be included " +
41834529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    "in content values. Contact IDs are assigned automatically");
41844529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
418573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
418697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
418797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
418897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0");
418997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
419097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
41914529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = 0;
41925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor cursor = mActiveDb.get().query(Views.RAW_CONTACTS,
419351bf5ea9531b9da72caff607dbdf35fd6f61cbe2Jeff Sharkey                new String[] { RawContacts._ID }, selection,
41944529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                selectionArgs, null, null, null);
41954529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        try {
41964529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            while (cursor.moveToNext()) {
41974529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                long rawContactId = cursor.getLong(0);
4198dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContact(rawContactId, values, callerIsSyncAdapter);
41994529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count++;
42004529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            }
42014529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        } finally {
42024529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            cursor.close();
42034529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
42044529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
42054529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return count;
42064529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    }
42074529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
4208dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContact(long rawContactId, ContentValues values,
4209dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
421096b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        final String selection = RawContacts._ID + " = ?";
421196b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(rawContactId);
421219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final boolean requestUndoDelete = (values.containsKey(RawContacts.DELETED)
421319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                && values.getAsInteger(RawContacts.DELETED) == 0);
421419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int previousDeleted = 0;
4215ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType = null;
4216ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName = null;
421743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String dataSet = null;
421819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        if (requestUndoDelete) {
42195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            Cursor cursor = mActiveDb.get().query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS,
42205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    selection, mSelectionArgs1, null, null, null);
422119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            try {
422219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                if (cursor.moveToFirst()) {
422319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    previousDeleted = cursor.getInt(RawContactsQuery.DELETED);
4224ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountType = cursor.getString(RawContactsQuery.ACCOUNT_TYPE);
4225ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountName = cursor.getString(RawContactsQuery.ACCOUNT_NAME);
422643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    dataSet = cursor.getString(RawContactsQuery.DATA_SET);
422719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                }
422819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            } finally {
422919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                cursor.close();
423019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
423119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
423219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT);
423319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        }
4234f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
42355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int count = mActiveDb.get().update(Tables.RAW_CONTACTS, values, selection, mSelectionArgs1);
42365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count != 0) {
4237f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            if (values.containsKey(RawContacts.AGGREGATION_MODE)) {
4238f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                int aggregationMode = values.getAsInteger(RawContacts.AGGREGATION_MODE);
4239f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov
4240f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // As per ContactsContract documentation, changing aggregation mode
4241f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // to DEFAULT should not trigger aggregation
4242f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                if (aggregationMode != RawContacts.AGGREGATION_MODE_DEFAULT) {
42435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mAggregator.get().markForAggregation(rawContactId, aggregationMode, false);
4244f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                }
4245f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            }
4246433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            if (values.containsKey(RawContacts.STARRED)) {
4247dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter) {
4248dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
4249dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            values.getAsLong(RawContacts.STARRED) != 0);
4250dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
42515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mAggregator.get().updateStarred(rawContactId);
4252dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
4253dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // if this raw contact is being associated with an account, then update the
4254dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // favorites group membership based on whether or not this contact is starred.
4255dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // If it is starred, add a group membership, if one doesn't already exist
4256dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // otherwise delete any matching group memberships.
4257dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
42585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    boolean starred = 0 != DatabaseUtils.longForQuery(mActiveDb.get(),
4259dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            SELECTION_STARRED_FROM_RAW_CONTACTS,
4260dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            new String[]{Long.toString(rawContactId)});
4261dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId, starred);
4262dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
4263dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
4264dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
4265dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // if this raw contact is being associated with an account, then add a
4266dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // group membership to the group marked as AutoAdd, if any.
4267dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
4268dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                addAutoAddMembership(rawContactId);
4269433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            }
4270dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
4271285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (values.containsKey(RawContacts.SOURCE_ID)) {
42725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mAggregator.get().updateLookupKeyForRawContact(mActiveDb.get(), rawContactId);
4273285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
4274f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            if (values.containsKey(RawContacts.NAME_VERIFIED)) {
4275f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
4276f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // If setting NAME_VERIFIED for this raw contact, reset it for all
4277f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // other raw contacts in the same aggregate
4278f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                if (values.getAsInteger(RawContacts.NAME_VERIFIED) != 0) {
42795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mDbHelper.get().resetNameVerifiedForOtherRawContacts(rawContactId);
4280f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                }
42815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mAggregator.get().updateDisplayNameForRawContact(mActiveDb.get(), rawContactId);
4282f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            }
428319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            if (requestUndoDelete && previousDeleted == 1) {
42845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mTransactionContext.get().rawContactInserted(rawContactId,
428543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        new AccountWithDataSet(accountName, accountType, dataSet));
428619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
42875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
42885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return count;
428933b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
429033b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
4291321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(Uri uri, ContentValues values, String selection,
4292f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
429320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
429420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
429520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
42965ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
429720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
429820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
429920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
430020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
430120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
43025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.get().getPackageId(packageName));
430320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
430420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
430597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
430697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
430797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    Data.IS_READ_ONLY + "=0");
430897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
430997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
4310653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        int count = 0;
431120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
4312653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
4313653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // so we don't need to worry about updating data we don't have permission to read.
43145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = queryLocal(uri,
4315f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                DataRowHandler.DataUpdateQuery.COLUMNS,
43165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                selection, selectionArgs, null, -1 /* directory ID */);
4317653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        try {
4318653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            while(c.moveToNext()) {
4319f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count += updateData(mValues, c, callerIsSyncAdapter);
432020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
4321653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        } finally {
4322653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            c.close();
432320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
432420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
4325653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return count;
432620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
432720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
4328f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
4329653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        if (values.size() == 0) {
4330653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return 0;
4331321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
4332653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
4333f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        final String mimeType = c.getString(DataRowHandler.DataUpdateQuery.MIMETYPE);
4334a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
4335f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        boolean updated =
43365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                rowHandler.update(mActiveDb.get(), mTransactionContext.get(), values, c,
43375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        callerIsSyncAdapter);
4338f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
4339f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
4340a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
4341f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        return updated ? 1 : 0;
4342321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    }
4343321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
43448c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(ContentValues values, String selection,
4345dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
43468c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        int count = 0;
43475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor cursor = mActiveDb.get().query(Views.CONTACTS,
43485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new String[] { Contacts._ID }, selection, selectionArgs, null, null, null);
43498c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        try {
43508c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            while (cursor.moveToNext()) {
43518c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                long contactId = cursor.getLong(0);
435224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
4353dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateContactOptions(contactId, values, callerIsSyncAdapter);
43548c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count++;
43558c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            }
43568c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        } finally {
43578c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            cursor.close();
43588c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        }
43598c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
43608c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return count;
43618c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
43628c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
4363dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateContactOptions(long contactId, ContentValues values,
4364dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
4365d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
43668c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
4367b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
4368d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
4369b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
4370d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
4371b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
4372d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
4373b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
4374d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
4375b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
4376d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.STARRED);
4377d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
4378d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
43798c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.size() == 0) {
4380d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
4381d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
4382d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
43838c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.containsKey(RawContacts.STARRED)) {
4384c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            // Mark dirty when changing starred to trigger sync
43858c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            mValues.put(RawContacts.DIRTY, 1);
4386c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey        }
4387c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey
43884da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(contactId);
43895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.get().update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=?"
439097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                + " AND " + RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0", mSelectionArgs1);
43918c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
4392dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (mValues.containsKey(RawContacts.STARRED) && !callerIsSyncAdapter) {
43935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            Cursor cursor = mActiveDb.get().query(Views.RAW_CONTACTS,
4394dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[] { RawContacts._ID }, RawContacts.CONTACT_ID + "=?",
4395dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    mSelectionArgs1, null, null, null);
4396dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            try {
4397dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                while (cursor.moveToNext()) {
4398dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    long rawContactId = cursor.getLong(0);
4399dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
4400dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            mValues.getAsLong(RawContacts.STARRED) != 0);
4401dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
4402dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } finally {
4403dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                cursor.close();
4404dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
4405dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
4406dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
44078c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // Copy changeable values to prevent automatically managed fields from
44088c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // being explicitly updated by clients.
44098c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
4410b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
44118c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
4412b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
44138c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
4414b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
44158c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
4416b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
44178c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
4418b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
44198c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.STARRED);
44208c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
44215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int rslt = mActiveDb.get().update(Tables.CONTACTS, mValues, Contacts._ID + "=?",
44225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mSelectionArgs1);
44236e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori
44249b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        if (values.containsKey(Contacts.LAST_TIME_CONTACTED) &&
44259b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori                !values.containsKey(Contacts.TIMES_CONTACTED)) {
44265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
44275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
44289b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        }
44299b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        return rslt;
4430f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
4431d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
4432127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
4433127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
44340c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId1 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID1);
44350c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId2 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID2);
443680c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
4437ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId1;
4438ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId2;
44390c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (rcId1 < rcId2) {
44400c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId1;
44410c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId2;
44420c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
44430c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId1;
44440c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId2;
4445b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
4446127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
44470c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
44484da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[0] = String.valueOf(rawContactId1);
44494da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[1] = String.valueOf(rawContactId2);
44500c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.delete(Tables.AGGREGATION_EXCEPTIONS,
44514da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    AggregationExceptions.RAW_CONTACT_ID1 + "=? AND "
44524da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    + AggregationExceptions.RAW_CONTACT_ID2 + "=?", mSelectionArgs2);
44530c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
44546bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            ContentValues exceptionValues = new ContentValues(3);
44556bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
44560c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
44570c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
44580c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
44590c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    exceptionValues);
4460127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
4461127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
44625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().invalidateAggregationExceptionCache();
44635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().markForAggregation(rawContactId1,
446469cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
44655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().markForAggregation(rawContactId2,
446669cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
4467dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov
44685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().aggregateContact(mTransactionContext.get(), db, rawContactId1);
44695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().aggregateContact(mTransactionContext.get(), db, rawContactId2);
4470127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
4471127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
4472127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
4473127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
4474b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
4475b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
447670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    public void onAccountsUpdated(Account[] accounts) {
4477bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
44783826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
44793826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
4480bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected boolean updateAccountsInBackground(Account[] accounts) {
4481f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao        // TODO : Check the unit test.
4482e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov        boolean accountsChanged = false;
44835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
44845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.set(db);
44855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        db.beginTransaction();
448670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        try {
448743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            Set<AccountWithDataSet> existingAccountsWithDataSets =
448843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    findValidAccountsWithDataSets(Tables.ACCOUNTS);
4489743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov
449043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // Add a row to the ACCOUNTS table (with no data set) for each new account.
4491743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            for (Account account : accounts) {
449243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                AccountWithDataSet accountWithDataSet = new AccountWithDataSet(
449343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        account.name, account.type, null);
449443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                if (!existingAccountsWithDataSets.contains(accountWithDataSet)) {
4495e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    accountsChanged = true;
449643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
449743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    // Add an account entry with an empty data set to match the account.
44985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME
449943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            + ", " + RawContacts.ACCOUNT_TYPE + ", " + RawContacts.DATA_SET
450043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            + ") VALUES (?, ?, ?)",
450143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            new String[] {
450243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getAccountName(),
450343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getAccountType(),
450443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getDataSet()
450543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            });
4506743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                }
4507743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            }
450848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
450943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // Check each of the existing sub-accounts against the account list.  If the owning
451043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // account no longer exists, the sub-account and all its data should be deleted.
451143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            List<AccountWithDataSet> accountsWithDataSetsToDelete =
451243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    new ArrayList<AccountWithDataSet>();
451343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            List<Account> accountList = Arrays.asList(accounts);
451443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            for (AccountWithDataSet accountWithDataSet : existingAccountsWithDataSets) {
451543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                Account owningAccount = new Account(
451643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        accountWithDataSet.getAccountName(), accountWithDataSet.getAccountType());
451743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                if (!accountList.contains(owningAccount)) {
451843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    accountsWithDataSetsToDelete.add(accountWithDataSet);
451943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                }
452070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
452170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
452243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            if (!accountsWithDataSetsToDelete.isEmpty()) {
4523e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                accountsChanged = true;
452443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                for (AccountWithDataSet accountWithDataSet : accountsWithDataSetsToDelete) {
452543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    Log.d(TAG, "removing data for removed account " + accountWithDataSet);
452643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    String[] accountParams = new String[] {
452743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            accountWithDataSet.getAccountName(),
452843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            accountWithDataSet.getAccountType()
452943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    };
453043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    String[] accountWithDataSetParams = accountWithDataSet.getDataSet() == null
453143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            ? accountParams
453243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            : new String[] {
453343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getAccountName(),
453443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getAccountType(),
453543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getDataSet()
453643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            };
453743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    String groupsDataSetClause = " AND " + Groups.DATA_SET
453843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            + (accountWithDataSet.getDataSet() == null ? " IS NULL" : " = ?");
453943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    String rawContactsDataSetClause = " AND " + RawContacts.DATA_SET
454043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            + (accountWithDataSet.getDataSet() == null ? " IS NULL" : " = ?");
4541f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                    String settingsDataSetClause = " AND " + Settings.DATA_SET
4542f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                            + (accountWithDataSet.getDataSet() == null ? " IS NULL" : " = ?");
454343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
45445d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4545e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.GROUPS +
4546e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Groups.ACCOUNT_NAME + " = ?" +
454743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    " AND " + Groups.ACCOUNT_TYPE + " = ?" +
454843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    groupsDataSetClause, accountWithDataSetParams);
45495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4550e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.PRESENCE +
4551e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (" +
4552e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    "SELECT " + RawContacts._ID +
4553e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " FROM " + Tables.RAW_CONTACTS +
4554e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
455543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    " AND " + RawContacts.ACCOUNT_TYPE + " = ?" +
455643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    rawContactsDataSetClause + ")", accountWithDataSetParams);
45575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4558c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            "DELETE FROM " + Tables.STREAM_ITEM_PHOTOS +
4559c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            " WHERE " + StreamItemPhotos.STREAM_ITEM_ID + " IN (" +
4560c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    "SELECT " + StreamItems._ID +
4561c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    " FROM " + Tables.STREAM_ITEMS +
4562c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    " WHERE " + StreamItems.RAW_CONTACT_ID + " IN (" +
4563c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                            "SELECT " + RawContacts._ID +
4564c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                            " FROM " + Tables.RAW_CONTACTS +
4565c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                            " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
4566c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                            " AND " + RawContacts.ACCOUNT_TYPE + " = ?" +
4567c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                            rawContactsDataSetClause + "))",
4568c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            accountWithDataSetParams);
4569c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                    db.execSQL(
4570c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            "DELETE FROM " + Tables.STREAM_ITEMS +
4571c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            " WHERE " + StreamItems.RAW_CONTACT_ID + " IN (" +
4572c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    "SELECT " + RawContacts._ID +
4573c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    " FROM " + Tables.RAW_CONTACTS +
4574c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
4575c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    " AND " + RawContacts.ACCOUNT_TYPE + " = ?" +
4576c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    rawContactsDataSetClause + ")",
4577c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            accountWithDataSetParams);
4578c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                    db.execSQL(
4579e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.RAW_CONTACTS +
4580e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
458143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            " AND " + RawContacts.ACCOUNT_TYPE + " = ?" +
458243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            rawContactsDataSetClause, accountWithDataSetParams);
45835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4584e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.SETTINGS +
4585e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Settings.ACCOUNT_NAME + " = ?" +
4586f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                            " AND " + Settings.ACCOUNT_TYPE + " = ?" +
4587f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                            settingsDataSetClause, accountWithDataSetParams);
45885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4589e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.ACCOUNTS +
4590e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + "=?" +
459143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            " AND " + RawContacts.ACCOUNT_TYPE + "=?" +
459243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            rawContactsDataSetClause, accountWithDataSetParams);
45935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4594d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            "DELETE FROM " + Tables.DIRECTORIES +
4595d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            " WHERE " + Directory.ACCOUNT_NAME + "=?" +
459643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            " AND " + Directory.ACCOUNT_TYPE + "=?", accountParams);
45974458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    resetDirectoryCache();
4598e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                }
4599e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov
460033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // Find all aggregated contacts that used to contain the raw contacts
460133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // we have just deleted and see if they are still referencing the deleted
4602e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                // names or photos.  If so, fix up those contacts.
460333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                HashSet<Long> orphanContactIds = Sets.newHashSet();
46045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor cursor = db.rawQuery("SELECT " + Contacts._ID +
460533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " FROM " + Tables.CONTACTS +
460633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " WHERE (" + Contacts.NAME_RAW_CONTACT_ID + " NOT NULL AND " +
460769cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                Contacts.NAME_RAW_CONTACT_ID + " NOT IN " +
460869cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + RawContacts._ID +
460969cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.RAW_CONTACTS + "))" +
461033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " OR (" + Contacts.PHOTO_ID + " NOT NULL AND " +
461133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                                Contacts.PHOTO_ID + " NOT IN " +
461269cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + Data._ID +
461369cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.DATA + "))", null);
461433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                try {
461533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    while (cursor.moveToNext()) {
461633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        orphanContactIds.add(cursor.getLong(0));
461733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    }
461833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                } finally {
461933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    cursor.close();
462033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
462133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
462233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                for (Long contactId : orphanContactIds) {
46235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mAggregator.get().updateAggregateData(mTransactionContext.get(), contactId);
462433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
46255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mDbHelper.get().updateAllVisible();
4626bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov                updateSearchIndexInTransaction();
462733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            }
462833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
462943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // Now that we've done the account-based additions and subtractions from the Accounts
463043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // table, check for raw contacts that have been added with a data set and add Accounts
463143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // entries for those if necessary.
463243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            existingAccountsWithDataSets = findValidAccountsWithDataSets(Tables.ACCOUNTS);
463343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            Set<AccountWithDataSet> rawContactAccountsWithDataSets =
463443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    findValidAccountsWithDataSets(Tables.RAW_CONTACTS);
463543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            rawContactAccountsWithDataSets.removeAll(existingAccountsWithDataSets);
463643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
463743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // Any remaining raw contact sub-accounts need to be added to the Accounts table.
463843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            for (AccountWithDataSet accountWithDataSet : rawContactAccountsWithDataSets) {
463943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                accountsChanged = true;
464043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
464143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                // Add an account entry to match the raw contact.
46425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                db.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME
464343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + ", " + RawContacts.ACCOUNT_TYPE + ", " + RawContacts.DATA_SET
464443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + ") VALUES (?, ?, ?)",
464543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        new String[] {
464643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                accountWithDataSet.getAccountName(),
464743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                accountWithDataSet.getAccountType(),
464843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                accountWithDataSet.getDataSet()
464943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        });
465043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
465143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
4652e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            if (accountsChanged) {
465343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                // TODO: Should sync state take data set into consideration?
46545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mDbHelper.get().getSyncState().onAccountsChanged(db, accounts);
4655e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            }
46565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db.setTransactionSuccessful();
465770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } finally {
46585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db.endTransaction();
465970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
466073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.clear();
46613826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
46623826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (accountsChanged) {
46633826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateContactsAccountCount(accounts);
46643826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
46653826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
46663826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
4667afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov        return accountsChanged;
466870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
4669619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
46703826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateContactsAccountCount(Account[] accounts) {
46713826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        int count = 0;
46723826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        for (Account account : accounts) {
46733826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            if (isContactsAccount(account)) {
46743826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                count++;
46753826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            }
46763826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
46773826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mContactsAccountCount = count;
46783826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
46793826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
46803826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    protected boolean isContactsAccount(Account account) {
46813826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        final IContentService cs = ContentResolver.getContentService();
46823826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        try {
46833826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return cs.getIsSyncable(account, ContactsContract.AUTHORITY) > 0;
46843826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } catch (RemoteException e) {
46853826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            Log.e(TAG, "Cannot obtain sync flag for account: " + account, e);
46863826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return false;
46873826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
46883826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
46893826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
469072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void onPackageChanged(String packageName) {
4691bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_DIRECTORIES, packageName);
4692d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4693d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4694619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
469543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * Finds all distinct account types and data sets present in the specified table.
4696627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     */
469743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    private Set<AccountWithDataSet> findValidAccountsWithDataSets(String table) {
469843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        Set<AccountWithDataSet> accountsWithDataSets = new HashSet<AccountWithDataSet>();
46995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = mActiveDb.get().rawQuery(
470043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                "SELECT DISTINCT " + RawContacts.ACCOUNT_NAME + "," + RawContacts.ACCOUNT_TYPE +
470143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                "," + RawContacts.DATA_SET +
470243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                " FROM " + table, null);
4703627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
4704627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            while (c.moveToNext()) {
470591abbc9f691297594262d1f2d79acb744a66712cDave Santoro                if (!c.isNull(0) && !c.isNull(1)) {
470643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    accountsWithDataSets.add(
470743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            new AccountWithDataSet(c.getString(0), c.getString(1), c.getString(2)));
4708627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
4709627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
4710627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } finally {
4711627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            c.close();
4712627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
471343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        return accountsWithDataSets;
4714627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
4715627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
47164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
47174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
47184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
471915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
472015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mReadAccessLatch);
472115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
47225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Query the profile DB if appropriate.
47235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDb(uri)) {
47245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToProfileMode();
47255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return mProfileProvider.query(uri, projection, selection, selectionArgs, sortOrder);
47265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
47275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
47285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Otherwise proceed with a normal query against the contacts DB.
47295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        switchToContactMode();
47305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.set(mContactsHelper.getReadableDatabase());
4731d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String directory = getQueryParameter(uri, ContactsContract.DIRECTORY_PARAM_KEY);
4732385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directory == null) {
4733b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return addSnippetExtrasToCursor(uri,
47345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder, -1));
4735385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directory.equals("0")) {
4736b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return addSnippetExtrasToCursor(uri,
47373716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder,
47385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            Directory.DEFAULT));
4739d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        } else if (directory.equals("1")) {
4740b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return addSnippetExtrasToCursor(uri,
47413716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder,
47425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            Directory.LOCAL_INVISIBLE));
4743d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4744d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4745d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        DirectoryInfo directoryInfo = getDirectoryAuthority(directory);
4746d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo == null) {
4747a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            Log.e(TAG, "Invalid directory ID: " + uri);
4748a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            return null;
4749d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4750d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4751d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Builder builder = new Uri.Builder();
4752d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.scheme(ContentResolver.SCHEME_CONTENT);
4753d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.authority(directoryInfo.authority);
4754d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.encodedPath(uri.getEncodedPath());
4755d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountName != null) {
4756d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_NAME, directoryInfo.accountName);
4757d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4758d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountType != null) {
4759d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, directoryInfo.accountType);
4760d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
47612e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
47622e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limit = getLimit(uri);
47632e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        if (limit != null) {
47642e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov            builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, limit);
47652e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        }
47662e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
4767d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Uri directoryUri = builder.build();
476809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
476909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        if (projection == null) {
477009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            projection = getDefaultProjection(uri);
477109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
477209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
4773332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        Cursor cursor = getContext().getContentResolver().query(directoryUri, projection, selection,
4774d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                selectionArgs, sortOrder);
47756ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
47766ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (cursor == null) {
47776ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return null;
47786ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
47796ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
4780547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        CrossProcessCursor crossProcessCursor = getCrossProcessCursor(cursor);
4781547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        if (crossProcessCursor != null) {
4782b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return addSnippetExtrasToCursor(uri, cursor);
4783547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        } else {
4784b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return matrixCursorFromCursor(addSnippetExtrasToCursor(uri, cursor));
4785547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        }
47863716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    }
47873716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
4788b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private Cursor addSnippetExtrasToCursor(Uri uri, Cursor cursor) {
4789547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro
4790547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        // If the cursor doesn't contain a snippet column, don't bother wrapping it.
4791547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        if (cursor.getColumnIndex(SearchSnippetColumns.SNIPPET) < 0) {
4792b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return cursor;
4793547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        }
4794547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro
47953716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // Parse out snippet arguments for use when snippets are retrieved from the cursor.
47963716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String[] args = null;
47973716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String snippetArgs =
47983716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                getQueryParameter(uri, SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY);
47993716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        if (snippetArgs != null) {
48003716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            args = snippetArgs.split(",");
48013716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        }
48023716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
48033716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String query = uri.getLastPathSegment();
48043716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String startMatch = args != null && args.length > 0 ? args[0]
48053716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_START_MATCH;
48063716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String endMatch = args != null && args.length > 1 ? args[1]
48073716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_END_MATCH;
48083716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String ellipsis = args != null && args.length > 2 ? args[2]
48093716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_ELLIPSIS;
48103716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        int maxTokens = args != null && args.length > 3 ? Integer.parseInt(args[3])
48113716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_MAX_TOKENS;
48123716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
4813b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        // Snippet data is needed for the snippeting on the client side, so store it in the cursor
4814b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        if (cursor instanceof AbstractCursor && deferredSnippetingRequested(uri)){
4815b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            Bundle oldExtras = cursor.getExtras();
4816b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            Bundle extras = new Bundle();
4817b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            if (oldExtras != null) {
4818b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                extras.putAll(oldExtras);
4819b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            }
4820b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            extras.putString(ContactsContract.DEFERRED_SNIPPETING_QUERY, query);
4821b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
4822b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            ((AbstractCursor) cursor).setExtras(extras);
48235517770250b3afa4fd88b6869c3244680821d222Dave Santoro        }
4824b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        return cursor;
4825b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    }
4826b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
4827b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private Cursor addDeferredSnippetingExtra(Cursor cursor) {
4828b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        if (cursor instanceof AbstractCursor){
4829b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            Bundle oldExtras = cursor.getExtras();
4830b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            Bundle extras = new Bundle();
4831b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            if (oldExtras != null) {
4832b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                extras.putAll(oldExtras);
4833b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            }
4834b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            extras.putBoolean(ContactsContract.DEFERRED_SNIPPETING, true);
4835b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            ((AbstractCursor) cursor).setExtras(extras);
4836b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        }
4837b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        return cursor;
48386ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
48396ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
48406ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    private CrossProcessCursor getCrossProcessCursor(Cursor cursor) {
48416ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        Cursor c = cursor;
48426ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (c instanceof CrossProcessCursor) {
48436ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return (CrossProcessCursor) c;
48446ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else if (c instanceof CursorWindow) {
48456ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return getCrossProcessCursor(((CursorWrapper) c).getWrappedCursor());
48466ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else {
48476ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return null;
48486ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
48496ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
48506ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
48516ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    public MatrixCursor matrixCursorFromCursor(Cursor cursor) {
48526ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        MatrixCursor newCursor = new MatrixCursor(cursor.getColumnNames());
48536ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        int numColumns = cursor.getColumnCount();
48546ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        String data[] = new String[numColumns];
48556ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        cursor.moveToPosition(-1);
48566ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        while (cursor.moveToNext()) {
48576ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            for (int i = 0; i < numColumns; i++) {
48586ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov                data[i] = cursor.getString(i);
48596ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            }
48606ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            newCursor.addRow(data);
4861332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        }
48626ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        return newCursor;
4863d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4864d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4865d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final class DirectoryQuery {
4866d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
4867d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory._ID,
4868d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.DIRECTORY_AUTHORITY,
4869d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_NAME,
4870d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_TYPE
4871d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        };
4872d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4873d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int DIRECTORY_ID = 0;
4874d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int AUTHORITY = 1;
4875d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_NAME = 2;
4876d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_TYPE = 3;
4877d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4878d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4879d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
4880d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Reads and caches directory information for the database.
4881d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
4882d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private DirectoryInfo getDirectoryAuthority(String directoryId) {
48834458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized (mDirectoryCache) {
48844458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            if (!mDirectoryCacheValid) {
48854458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCache.clear();
48865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                SQLiteDatabase db = mDbHelper.get().getReadableDatabase();
488749d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                Cursor cursor = db.query(Tables.DIRECTORIES,
48884458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryQuery.COLUMNS,
48894458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        null, null, null, null, null);
48904458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                try {
48914458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    while (cursor.moveToNext()) {
48924458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryInfo info = new DirectoryInfo();
48934458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        String id = cursor.getString(DirectoryQuery.DIRECTORY_ID);
48944458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.authority = cursor.getString(DirectoryQuery.AUTHORITY);
48954458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
48964458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
48974458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        mDirectoryCache.put(id, info);
48984458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    }
48994458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                } finally {
49004458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    cursor.close();
4901d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                }
49024458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCacheValid = true;
4903d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4904d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
49054458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            return mDirectoryCache.get(directoryId);
49064458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
4907d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4908d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
490972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void resetDirectoryCache() {
49104458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized(mDirectoryCache) {
49114458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            mDirectoryCacheValid = false;
49124458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
491372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
491472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
49155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    protected Cursor queryLocal(Uri uri, String[] projection, String selection,
49165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            String[] selectionArgs, String sortOrder, long directoryId) {
4917bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
4918bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            Log.v(TAG, "query: " + uri);
4919bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        }
49200b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov
49215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
49225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
49235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.set(mDb);
49245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
492535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4926d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
49271f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
4928c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limit = getLimit(uri);
4929b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        boolean snippetDeferred = false;
4930c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
49312ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        // The expression used in bundleLetterCountExtras() to get count.
49322ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        String addressBookIndexerCountExpression = null;
49332ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki
4934a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
49354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
493635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
49375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE:
49385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().query(mActiveDb.get(), projection, selection,
49395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionArgs, sortOrder);
494035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4941d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
4942763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
49434b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                appendLocalDirectorySelectionIfNeeded(qb, directoryId);
4944619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
4945619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
4946619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
4947d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
49484a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
4949763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
49504da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
49514da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
49526bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
49536bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
49546bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
49555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP:
49565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
49575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
49585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int segmentCount = pathSegments.size();
49595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount < 3) {
49605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
4961fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
49625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
4963a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
49645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String lookupKey = pathSegments.get(2);
49655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount == 4) {
49665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
49675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
4968763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
4969a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
49705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
4971a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4972a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
4973a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
49745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        return c;
49755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
49765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
49775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
4978763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
49794da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
49805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        String.valueOf(lookupContactIdByLookupKey(mActiveDb.get(), lookupKey)));
49814da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
49825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                break;
49835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
49845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
49852149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_DATA:
4986bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_ID_DATA:
4987bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_PHOTO:
4988bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_ID_PHOTO: {
49892149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
49902149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                int segmentCount = pathSegments.size();
49912149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount < 4) {
49925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
49932149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                            "Missing a lookup key", uri));
49942149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
49952149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
49962149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount == 5) {
49972149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
49982149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
49992149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    setTablesAndProjectionMapForData(lookupQb, uri, projection, false);
5000bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    if (match == CONTACTS_LOOKUP_PHOTO || match == CONTACTS_LOOKUP_ID_PHOTO) {
5001bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
5002bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    }
5003a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
50045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
5005a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
5006a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Data.CONTACT_ID, contactId, Data.LOOKUP_KEY, lookupKey);
5007a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
50082149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                        return c;
50092149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    }
50102149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
50112149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    // TODO see if the contact exists but has no data rows (rare)
50122149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
50132149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
50142149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
50155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
50162149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
501724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        String.valueOf(contactId));
5018bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                if (match == CONTACTS_LOOKUP_PHOTO || match == CONTACTS_LOOKUP_ID_PHOTO) {
5019bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
5020bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                }
50212149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                qb.appendWhere(" AND " + Data.CONTACT_ID + "=?");
50222149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                break;
50232149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            }
50242149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
50253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_ID_STREAM_ITEMS: {
50263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                long contactId = Long.parseLong(uri.getPathSegments().get(1));
50273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
50283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
5029af10329f85c5d8c4196c495a9f0f9a6c6ecbc231Daniel Lehmann                qb.appendWhere(StreamItems.CONTACT_ID + "=?");
50303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
50313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
50323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
50333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_LOOKUP_STREAM_ITEMS:
50343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_LOOKUP_ID_STREAM_ITEMS: {
50353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                List<String> pathSegments = uri.getPathSegments();
50363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                int segmentCount = pathSegments.size();
50373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                if (segmentCount < 4) {
50385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
50393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                            "Missing a lookup key", uri));
50403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
50413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String lookupKey = pathSegments.get(2);
50423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                if (segmentCount == 5) {
50433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    long contactId = Long.parseLong(pathSegments.get(3));
50443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
50453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    setTablesAndProjectionMapForStreamItems(lookupQb);
50465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
50473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
5048af10329f85c5d8c4196c495a9f0f9a6c6ecbc231Daniel Lehmann                            StreamItems.CONTACT_ID, contactId,
5049af10329f85c5d8c4196c495a9f0f9a6c6ecbc231Daniel Lehmann                            StreamItems.CONTACT_LOOKUP_KEY, lookupKey);
50503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    if (c != null) {
50513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        return c;
50523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    }
50533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
50543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
50553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
50565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
50573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
50583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(RawContacts.CONTACT_ID + "=?");
50593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
50603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
50613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
5062f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
506342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
50645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
5065ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
5066f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                qb.setProjectionMap(sContactsVCardProjectionMap);
50674da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
506824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        String.valueOf(contactId));
50694da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
5070f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                break;
5071f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            }
5072f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
507342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
507442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
507542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                String currentDateString = dateFormat.format(new Date()).toString();
50765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mActiveDb.get().rawQuery(
507742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    "SELECT" +
507842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " 'vcards_' || ? || '.vcf' AS " + OpenableColumns.DISPLAY_NAME + "," +
507942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " NULL AS " + OpenableColumns.SIZE,
508042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    new String[] { currentDateString });
508142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
508242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
5083ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_FILTER: {
5084916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                String filterParam = "";
5085b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                boolean deferredSnipRequested = deferredSnippetingRequested(uri);
5086ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
5087916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    filterParam = uri.getLastPathSegment();
5088ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
50897ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov                setTablesAndProjectionMapForContactsWithSnippet(
5090b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                        qb, uri, projection, filterParam, directoryId,
5091b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                        deferredSnipRequested);
5092b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                snippetDeferred = isSingleWordQuery(filterParam) &&
5093b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                        deferredSnipRequested && snippetNeeded(projection);
5094ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
5095ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
5096ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
5097ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT_FILTER:
5098ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT: {
50992f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Basically the resultant SQL should look like this:
51002f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // (SQL for listing starred items)
51012f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // UNION ALL
51022f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // (SQL for listing frequently contacted items)
51032f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // ORDER BY ...
51042f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
51052f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final boolean phoneOnly = readBooleanQueryParameter(
51062f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        uri, ContactsContract.STREQUENT_PHONE_ONLY, false);
51072f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (match == CONTACTS_STREQUENT_FILTER && uri.getPathSegments().size() > 3) {
51084a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
51094a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
5110e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
51115e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
51122f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    selection = DbQueryUtils.concatenateClauses(selection, sb.toString());
51134a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
51144a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
51152f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                String[] subProjection = null;
51165e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                if (projection != null) {
51172f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    subProjection = appendProjectionArg(projection, TIMES_USED_SORT_COLUMN);
51185e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                }
51195e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
51204a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                // Build the first query for starred
51214928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                setTablesAndProjectionMapForContacts(qb, uri, projection, false);
51224928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                qb.setProjectionMap(phoneOnly ?
51234928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                        sStrequentPhoneOnlyStarredProjectionMap
51244928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                        : sStrequentStarredProjectionMap);
51259dbfd650ccf93714f3266e80f9fbdbcb526ae7b3Daisuke Miyakawa                if (phoneOnly) {
51265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    qb.appendWhere(DbQueryUtils.concatenateClauses(
51275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            selection, Contacts.HAS_PHONE_NUMBER + "=1"));
51289dbfd650ccf93714f3266e80f9fbdbcb526ae7b3Daisuke Miyakawa                }
51292f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                qb.setStrict(true);
51302f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final String starredQuery = qb.buildQuery(subProjection,
513124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        Contacts.STARRED + "=1", Contacts._ID, null, null, null);
5132d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
51332f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Reset the builder.
5134d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
51352f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                qb.setStrict(true);
51364928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
51374928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                // Build the second query for frequent part.
51384928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                final String frequentQuery;
51394928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                if (phoneOnly) {
51404928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    final StringBuilder tableBuilder = new StringBuilder();
51414928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // In phone only mode, we need to look at view_data instead of
51424928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // contacts/raw_contacts to obtain actual phone numbers. One problem is that
51434928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // view_data is much larger than view_contacts, so our query might become much
51444928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // slower.
51454928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    //
51464928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // To avoid the possible slow down, we start from data usage table and join
51474928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // view_data to the table, assuming data usage table is quite smaller than
51484928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // data rows (almost always it should be), and we don't want any phone
51494928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // numbers not used by the user. This way sqlite is able to drop a number of
51504928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // rows in view_data in the early stage of data lookup.
51514928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    tableBuilder.append(Tables.DATA_USAGE_STAT
51524928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + " INNER JOIN " + Views.DATA + " " + Tables.DATA
51534928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "="
51544928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                                + DataColumns.CONCRETE_ID + " AND "
51554928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "="
51564928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                                + DataUsageStatColumns.USAGE_TYPE_INT_CALL + ")");
51574928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    appendContactPresenceJoin(tableBuilder, projection, RawContacts.CONTACT_ID);
51584928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    appendContactStatusUpdateJoin(tableBuilder, projection,
51594928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            ContactsColumns.LAST_STATUS_UPDATE_ID);
51604928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
51614928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setTables(tableBuilder.toString());
51624928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setProjectionMap(sStrequentPhoneOnlyFrequentProjectionMap);
51634928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.appendWhere(DbQueryUtils.concatenateClauses(
51644928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            selection,
51654928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            Contacts.STARRED + "=0 OR " + Contacts.STARRED + " IS NULL",
51664928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            MimetypesColumns.MIMETYPE + " IN ("
51674928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + "'" + Phone.CONTENT_ITEM_TYPE + "', "
51684928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + "'" + SipAddress.CONTENT_ITEM_TYPE + "')"));
51694928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    frequentQuery = qb.buildQuery(subProjection, null, null, null, null, null);
51704928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                } else {
51714928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    setTablesAndProjectionMapForContacts(qb, uri, projection, true);
51724928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setProjectionMap(sStrequentFrequentProjectionMap);
51734928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.appendWhere(DbQueryUtils.concatenateClauses(
51744928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            selection,
51755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            "(" + Contacts.STARRED + " =0 OR " + Contacts.STARRED + " IS NULL)"));
51764928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    frequentQuery = qb.buildQuery(subProjection,
51774928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            null, Contacts._ID, null, null, null);
51784928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                }
5179d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
5180d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
51812f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final String unionQuery =
51822f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
51832f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                                STREQUENT_ORDER_BY, STREQUENT_LIMIT);
51842f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
51852f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Here, we need to use selection / selectionArgs (supplied from users) "twice",
51862f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // as we want them both for starred items and for frequently contacted items.
51872f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                //
51882f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // e.g. if the user specify selection = "starred =?" and selectionArgs = "0",
51892f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // the resultant SQL should be like:
51902f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // SELECT ... WHERE starred =? AND ...
51912f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // UNION ALL
51922f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // SELECT ... WHERE starred =? AND ...
51932f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                String[] doubledSelectionArgs = null;
51942f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (selectionArgs != null) {
51952f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    final int length = selectionArgs.length;
51962f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    doubledSelectionArgs = new String[length * 2];
51977d7d0e95636344c01eb4e4d034791c199bee98e9Daisuke Miyakawa                    System.arraycopy(selectionArgs, 0, doubledSelectionArgs, 0, length);
51987d7d0e95636344c01eb4e4d034791c199bee98e9Daisuke Miyakawa                    System.arraycopy(selectionArgs, 0, doubledSelectionArgs, length, length);
51992f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                }
52002f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
52015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor cursor = mActiveDb.get().rawQuery(unionQuery, doubledSelectionArgs);
52022f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (cursor != null) {
52032f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    cursor.setNotificationUri(getContext().getContentResolver(),
5204d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
5205d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
52062f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                return cursor;
5207d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
5208d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
520945ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa            case CONTACTS_FREQUENT: {
521045ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                setTablesAndProjectionMapForContacts(qb, uri, projection, true);
521145ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                qb.setProjectionMap(sStrequentFrequentProjectionMap);
521245ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                groupBy = Contacts._ID;
521345ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                if (!TextUtils.isEmpty(sortOrder)) {
521445ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                    sortOrder = FREQUENT_ORDER_BY + ", " + sortOrder;
521545ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                } else {
521645ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                    sortOrder = FREQUENT_ORDER_BY;
521745ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                }
521845ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                break;
521945ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa            }
522045ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa
5221ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_GROUP: {
5222763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
5223b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
522471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
52257cf50494501938f175d288077145acf49da8f171Daniel Lehmann                    String groupMimeTypeId = String.valueOf(
52267cf50494501938f175d288077145acf49da8f171Daniel Lehmann                            mDbHelper.get().getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
52274a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
52287cf50494501938f175d288077145acf49da8f171Daniel Lehmann                    selectionArgs = insertSelectionArg(selectionArgs, groupMimeTypeId);
5229b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
5230b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
5231b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
5232b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
523324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
523424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForContacts(qb, uri, projection);
523524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
523624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
523724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
523824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_ENTITIES: {
523924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForEntities(qb, uri, projection);
524024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
524124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
524224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
524324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD: {
5244ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
524524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.setProjectionMap(sContactsVCardProjectionMap);
524624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
524724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
524824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
5249a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_DATA: {
52504a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
525182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
52524da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
52534da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
52546bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
52556bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
525600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
5257a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
52583653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
525982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
52604da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
52614da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
52623653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
52633653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                break;
52643653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
52653653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
5266a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_ENTITIES: {
5267a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
5268a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
5269a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
5270a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
5271a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
5272a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
5273a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5274a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ENTITIES:
5275a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_ENTITIES: {
5276a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
5277a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                int segmentCount = pathSegments.size();
5278a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount < 4) {
52795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
5280a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            "Missing a lookup key", uri));
5281a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
5282a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
5283a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount == 5) {
5284a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
5285a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
5286a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    setTablesAndProjectionMapForEntities(lookupQb, uri, projection);
5287a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
5288a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
52895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
5290a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
5291a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.CONTACT_ID, contactId,
5292a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.LOOKUP_KEY, lookupKey);
5293a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
5294a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        return c;
5295a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    }
5296a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
5297a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5298a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
5299a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
53005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        String.valueOf(lookupContactIdByLookupKey(mActiveDb.get(), lookupKey)));
5301a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + Contacts.Entity.CONTACT_ID + "=?");
5302a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
5303a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
5304a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
53053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
53063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
53073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
53083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
53093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
53103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
53113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
53123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
53139b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann                qb.appendWhere(StreamItems._ID + "=?");
53143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
53153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
53163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
53173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_LIMIT: {
53186802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                MatrixCursor cursor = new MatrixCursor(new String[]{StreamItems.MAX_ITEMS}, 1);
53196802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                cursor.addRow(new Object[]{MAX_STREAM_ITEMS_PER_RAW_CONTACT});
53203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return cursor;
53213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
53223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
53233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
53243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
53253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
53263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
53273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
53283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
53293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
53303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
53313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemId);
53323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=?");
53333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
53343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
53353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
53363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
53373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
53383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
53393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
53403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemPhotoId);
53413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemId);
53423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=? AND " +
53433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=?");
53443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
53453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
53463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
5347f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case PHOTO_DIMENSIONS: {
5348f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                MatrixCursor cursor = new MatrixCursor(
5349f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{DisplayPhoto.DISPLAY_MAX_DIM, DisplayPhoto.THUMBNAIL_MAX_DIM},
5350f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        1);
5351f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                cursor.addRow(new Object[]{mMaxDisplayPhotoDim, mMaxThumbnailPhotoDim});
5352f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                return cursor;
5353f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
5354f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
53554a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case PHONES: {
535682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
53577cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + "=" +
53587cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        mDbHelper.get().getMimeTypeIdForPhone());
53592ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki
53602ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                // Dedupe phone numbers per contact.
5361cf55cbe8932f620484a3634d13ecc116c32fdc99Daisuke Miyakawa                groupBy = RawContacts.CONTACT_ID + ", " + Data.DATA1;
53622ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki
53632ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                // In this case, because we dedupe phone numbers, the address book indexer needs
53642ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                // to take it into account too.  (Otherwise headers will appear in wrong positions.)
53652ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                // So use count(distinct pair(CONTACT_ID, PHONE NUMBER)) instead of count(*).
53662ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                // But because there's no such thing as pair() on sqlite, we use
53672ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                // CONTACT_ID || ',' || PHONE NUMBER instead.
53682ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                // This only slows down the query by 14% with 10,000 contacts.
53692ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                addressBookIndexerCountExpression = "DISTINCT "
53702ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                        + RawContacts.CONTACT_ID + "||','||" + Data.DATA1;
53712815f58f72f109790585931f601a63ddc02536a5Evan Millar                break;
53722815f58f72f109790585931f601a63ddc02536a5Evan Millar            }
53732815f58f72f109790585931f601a63ddc02536a5Evan Millar
537448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID: {
537582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
53764da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
53777cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
53787cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForPhone());
53794da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
538048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
538148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
538248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
5383ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
538446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
538546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                Integer typeInt = sDataUsageTypeMap.get(typeParam);
538646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (typeInt == null) {
538746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    typeInt = DataUsageStatColumns.USAGE_TYPE_INT_CALL;
538846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
538946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
53907cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
53917cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForPhone());
5392ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
53934a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
53944a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
5395a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    sb.append(" AND (");
53965e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
539745d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    boolean hasCondition = false;
53985e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    boolean orNeeded = false;
53995e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String normalizedName = NameNormalizer.normalize(filterParam);
54005e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (normalizedName.length() > 0) {
5401155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN " +
5402155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
5403155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.SEARCH_INDEX +
5404155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " JOIN " + Tables.RAW_CONTACTS +
5405155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
5406155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
5407155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE " + SearchIndexColumns.NAME + " MATCH ");
54082352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                        DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filterParam) + "*");
5409155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(")");
54105e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        orNeeded = true;
541145d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
54125e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
54135e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
5414892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    String number = PhoneNumberUtils.normalizeNumber(filterParam);
5415892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (!TextUtils.isEmpty(number)) {
54165e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        if (orNeeded) {
54175e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                            sb.append(" OR ");
54185e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        }
54195e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data._ID +
5420892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                " IN (SELECT DISTINCT " + PhoneLookupColumns.DATA_ID
5421892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " FROM " + Tables.PHONE_LOOKUP
5422892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
5423892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append(number);
5424892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append("%')");
542545d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
542645d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    }
542745d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov
542845d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    if (!hasCondition) {
542945d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // If it is neither a phone number nor a name, the query should return
543045d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // an empty cursor.  Let's ensure that.
543145d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        sb.append("0");
54325e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
54335e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
5434a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
5435ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
543658567abca253f1efa2db5c39e17e42dca589e916Daisuke Miyakawa                groupBy = "(CASE WHEN " + PhoneColumns.NORMALIZED_NUMBER
543758567abca253f1efa2db5c39e17e42dca589e916Daisuke Miyakawa                        + " IS NOT NULL THEN " + PhoneColumns.NORMALIZED_NUMBER
543858567abca253f1efa2db5c39e17e42dca589e916Daisuke Miyakawa                        + " ELSE " + Phone.NUMBER + " END), " + RawContacts.CONTACT_ID;
5439a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
544046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
544146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
544246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = accountPromotionSortOrder + ", " + PHONE_FILTER_SORT_ORDER;
544346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    } else {
544446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = PHONE_FILTER_SORT_ORDER;
544546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    }
5446a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
5447ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
5448ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
5449ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
54504a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case EMAILS: {
545182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
54527cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
54537cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForEmail());
54544a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                break;
54554a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
54564a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
545748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID: {
545882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
54594da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
54607cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
54617cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForEmail()
54624da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        + " AND " + Data._ID + "=?");
546348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
546448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
546548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
54665e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_LOOKUP: {
546782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
54687cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
54697cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForEmail());
54704a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
547108768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String email = uri.getLastPathSegment();
54725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    String address = mDbHelper.get().extractAddressFromEmailAddress(email);
547308768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, address);
547408768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    qb.appendWhere(" AND UPPER(" + Email.DATA + ")=UPPER(?)");
54754a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
5476ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
5477ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
5478ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
54795e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_FILTER: {
548046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
548146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                Integer typeInt = sDataUsageTypeMap.get(typeParam);
548246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (typeInt == null) {
548346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    typeInt = DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT;
548446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
548546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
548607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                String filterParam = null;
54877d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa
548807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
548907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    filterParam = uri.getLastPathSegment();
549007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    if (TextUtils.isEmpty(filterParam)) {
549107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                        filterParam = null;
549207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    }
549307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                }
54945e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
549507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (filterParam == null) {
549607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    // If the filter is unspecified, return nothing
549707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    qb.appendWhere(" AND 0");
549807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                } else {
549907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    StringBuilder sb = new StringBuilder();
550007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(" AND " + Data._ID + " IN (");
550107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(
550207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            "SELECT " + Data._ID +
550307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " FROM " + Tables.DATA +
55042a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                            " WHERE " + DataColumns.MIMETYPE_ID + "=");
55055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    sb.append(mDbHelper.get().getMimeTypeIdForEmail());
55062a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                    sb.append(" AND " + Data.DATA1 + " LIKE ");
550707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(sb, filterParam + '%');
550820938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                    if (!filterParam.contains("@")) {
5509155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(
5510155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " UNION SELECT " + Data._ID +
5511155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.DATA +
5512155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE +" + DataColumns.MIMETYPE_ID + "=");
55135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        sb.append(mDbHelper.get().getMimeTypeIdForEmail());
5514155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(" AND " + Data.RAW_CONTACT_ID + " IN " +
5515155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
5516155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.SEARCH_INDEX +
5517155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " JOIN " + Tables.RAW_CONTACTS +
5518155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
5519155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
5520155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE " + SearchIndexColumns.NAME + " MATCH ");
55212352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                        DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filterParam) + "*");
5522155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(")");
55235e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
55245e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
5525a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
55265e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                }
55275e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
5528a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
552946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
553046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
553146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = accountPromotionSortOrder + ", " + EMAIL_FILTER_SORT_ORDER;
55327d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                    } else {
55337d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                        sortOrder = EMAIL_FILTER_SORT_ORDER;
55347d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                    }
5535a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
55365e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                break;
55375e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
55385e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
5539ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
554082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
55417cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
55427cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForStructuredPostal());
5543ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
5544ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
5545ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
554648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
554782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
55484da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
55497cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
55507cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForStructuredPostal());
55514da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
555248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
555348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
555448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
5555d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS:
5556d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS: {
5557763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
55584f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
55594f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
55604f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
5561d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS_ID:
5562d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID: {
55635ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
5564763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
55654da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
55664da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
55674f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
55684f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
55694f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
5570d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS_DATA:
5571d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID_DATA: {
5572d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                int segment = match == RAW_CONTACTS_DATA ? 1 : 2;
5573d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(segment));
557482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
55754da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
55764da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=?");
557724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
557824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
557924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
55803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case RAW_CONTACTS_ID_STREAM_ITEMS: {
55813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
55823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
55833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
55843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItems.RAW_CONTACT_ID + "=?");
55853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
55863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
558724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
558882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            case RAW_CONTACTS_ID_STREAM_ITEMS_ID: {
558982780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
559082780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                long streamItemId = Long.parseLong(uri.getPathSegments().get(3));
559182780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                setTablesAndProjectionMapForStreamItems(qb);
559282780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(streamItemId));
5593c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
559482780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                qb.appendWhere(StreamItems.RAW_CONTACT_ID + "=? AND " +
559582780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        StreamItems._ID + "=?");
559682780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                break;
559782780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            }
559882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
559924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID_ENTITIES: {
560024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(2));
560124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
560224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForRawEntities(qb, uri);
56035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                qb.appendWhere(" AND " + RawContacts._ID + "=?");
5604e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
5605e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
5606e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
5607d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case DATA:
5608d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_DATA: {
560982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
5610e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
5611e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
5612e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
5613d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case DATA_ID:
5614d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_DATA_ID: {
561582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
56164da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
56174da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
5618a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                break;
5619a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            }
5620a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov
5621a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
56224a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
5623a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
5624a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
5625a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
5626892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    sortOrder = " length(lookup.normalized_number) DESC";
5627a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
5628a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
5629e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
5630e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                String numberE164 = PhoneNumberUtils.formatNumberToE164(number,
56315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mDbHelper.get().getCurrentCountryIso());
5632892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                String normalizedNumber =
5633892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        PhoneNumberUtils.normalizeNumber(number);
56345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mDbHelper.get().buildPhoneLookupAndContactQuery(qb, normalizedNumber, numberE164);
5635e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                qb.setProjectionMap(sPhoneLookupProjectionMap);
5636e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                // Phone lookup cannot be combined with a selection
5637e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selection = null;
5638e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selectionArgs = null;
5639a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
5640a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
5641a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
5642ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
5643ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.GROUPS);
5644ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
5645f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                appendAccountFromParameter(qb, uri);
5646ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5647ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5648ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5649ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
5650ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.GROUPS);
5651ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
56524da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
56534da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Groups._ID + "=?");
5654ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5655ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5656ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5657ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
5658f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                final boolean returnGroupCountPerAccount =
5659f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        readBooleanQueryParameter(uri, Groups.PARAM_RETURN_GROUP_COUNT_PER_ACCOUNT,
5660f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                                false);
5661f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                qb.setTables(Views.GROUPS + " AS " + Tables.GROUPS);
5662f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                qb.setProjectionMap(returnGroupCountPerAccount ?
5663f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        sGroupsSummaryProjectionMapWithGroupCountPerAccount
5664f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        : sGroupsSummaryProjectionMap);
5665f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                appendAccountFromParameter(qb, uri);
5666f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                groupBy = GroupsColumns.CONCRETE_ID;
5667ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5668ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5669ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5670b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
56710c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
5672b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
5673b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
5674b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
5675b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
567631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
5677d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
56782d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                String filter = null;
56792d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
56802d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                    filter = uri.getPathSegments().get(3);
56812d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                }
568231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
5683d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                if (limit != null) {
5684d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(limit);
568531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
568631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
568731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
568831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
56895b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                ArrayList<AggregationSuggestionParameter> parameters = null;
56905b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                List<String> query = uri.getQueryParameters("query");
56915b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                if (query != null && !query.isEmpty()) {
56925b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    parameters = new ArrayList<AggregationSuggestionParameter>(query.size());
56935b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    for (String parameter : query) {
56945b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        int offset = parameter.indexOf(':');
56955b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        parameters.add(offset == -1
56965b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                ? new AggregationSuggestionParameter(
569776dfa406e2cde19c824983c37fc92c1c5bf63eecDaniel Lehmann                                        AggregationSuggestions.PARAMETER_MATCH_NAME,
56985b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter)
56995b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                : new AggregationSuggestionParameter(
57005b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(0, offset),
57015b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(offset + 1)));
57025b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    }
57035b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                }
57045b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
5705763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
57067581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov
57075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mAggregator.get().queryAggregationSuggestions(qb, projection, contactId,
57085b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        maxSuggestions, filter, parameters);
570931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
571031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
5711eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
5712eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setTables(Tables.SETTINGS);
5713eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setProjectionMap(sSettingsProjectionMap);
5714f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                appendAccountFromParameter(qb, uri);
5715e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
5716e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // When requesting specific columns, this query requires
5717e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // late-binding of the GroupMembership MIME-type.
57185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                final String groupMembershipMimetypeId = Long.toString(mDbHelper.get()
5719e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
572082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
57215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mDbHelper.get().isInProjection(projection, Settings.UNGROUPED_COUNT)) {
5722e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
5723e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
572482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
57255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mDbHelper.get().isInProjection(
57265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                                projection, Settings.UNGROUPED_WITH_PHONES)) {
5727e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
5728e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
5729e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
5730eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
5731eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
5732eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
57335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case STATUS_UPDATES:
57345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_STATUS_UPDATES: {
57350a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
57365ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
57375ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
57385ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
573982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES_ID: {
57400a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
57414da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
57424da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(DataColumns.CONCRETE_ID + "=?");
57435ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
57445ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
57455ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
5746c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS: {
5747174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(
57485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mActiveDb.get(), uri, projection, limit);
5749c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5750c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
5751c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT: {
57522d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                String lookupKey = uri.getLastPathSegment();
5753174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                String filter = getQueryParameter(
5754174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        uri, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
5755174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchShortcutRefresh(
57565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mActiveDb.get(), projection, lookupKey, filter);
5757c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5758c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
57591b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS:
5760ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
57611b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
57621b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
57631b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
57641b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_WITH_PHONES:
5765ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
57661b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
57671b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.HAS_PHONE_NUMBER + "=1");
57681b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
57691b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
57701b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_FAVORITES:
5771ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
57721b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
57731b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.STARRED + "=1");
57741b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
57751b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
57761b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_GROUP_NAME:
5777ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
57781b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
577971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
57807cf50494501938f175d288077145acf49da8f171Daniel Lehmann                String groupMimeTypeId = String.valueOf(
57817cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        mDbHelper.get().getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
57821b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
57837cf50494501938f175d288077145acf49da8f171Daniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, groupMimeTypeId);
57841b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
57851b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
57863202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro            case RAW_CONTACT_ENTITIES:
57873202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro            case PROFILE_RAW_CONTACT_ENTITIES: {
5788a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
578946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
579046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
579146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
579246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITY_ID: {
579346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
5794a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
57954da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
57964da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
579746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
579846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
579946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
580009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            case PROVIDER_STATUS: {
580109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                return queryProviderStatus(uri, projection);
580209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
580309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5804d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES : {
5805d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
5806d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
5807d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
5808d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
5809d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
5810d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID : {
5811385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                long id = ContentUris.parseId(uri);
5812d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
5813d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
5814385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(id));
5815d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.appendWhere(Directory._ID + "=?");
5816d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
5817d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
5818d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
58197a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            case COMPLETE_NAME: {
58207a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                return completeName(uri, projection);
58217a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
58227a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
58234f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
5824f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
5825c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                        sortOrder, limit);
58264f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
58274f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
582809e69522745551522c55dff27424496f255def46Daniel Lehmann        qb.setStrict(true);
58297f786e5cbde9975b9632beb9b6d19eeef8a64cf1Dmitri Plotnikov
5830ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        Cursor cursor =
58315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                query(mActiveDb.get(), qb, projection, selection, selectionArgs, sortOrder, groupBy,
58325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        limit);
5833ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (readBooleanQueryParameter(uri, ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, false)) {
58345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            cursor = bundleLetterCountExtras(cursor, mActiveDb.get(), qb, selection,
58352ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                    selectionArgs, sortOrder, addressBookIndexerCountExpression);
5836ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5837b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        if (snippetDeferred) {
5838b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            cursor = addDeferredSnippetingExtra(cursor);
5839b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        }
5840ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        return cursor;
58415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
58425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
58435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
58445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String selection, String[] selectionArgs, String sortOrder, String groupBy,
58455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String limit) {
5846038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        if (projection != null && projection.length == 1
5847038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                && BaseColumns._COUNT.equals(projection[0])) {
5848038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            qb.setProjectionMap(sCountProjectionMap);
5849038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
58505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
58515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sortOrder, limit);
58524f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
58534f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
58544f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
58554f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
58564f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
58574f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
585809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    /**
585909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     * Creates a single-row cursor containing the current status of the provider.
586009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     */
586109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private Cursor queryProviderStatus(Uri uri, String[] projection) {
586209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
586309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        RowBuilder row = cursor.newRow();
586409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
586509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            if (ProviderStatus.STATUS.equals(projection[i])) {
586609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mProviderStatus);
586709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            } else if (ProviderStatus.DATA1.equals(projection[i])) {
586809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mEstimatedStorageRequirement);
586909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
587009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        }
587109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        return cursor;
587209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    }
587309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5874a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /**
5875a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * Runs the query with the supplied contact ID and lookup ID.  If the query succeeds,
5876a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * it returns the resulting cursor, otherwise it returns null and the calling
5877a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * method needs to resolve the lookup key and rerun the query.
5878a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     */
5879a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private Cursor queryWithContactIdAndLookupKey(SQLiteQueryBuilder lookupQb,
5880a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            SQLiteDatabase db, Uri uri,
5881a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection, String selection, String[] selectionArgs,
5882a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String sortOrder, String groupBy, String limit,
5883a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn, long contactId, String lookupKeyColumn, String lookupKey) {
5884a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        String[] args;
5885a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (selectionArgs == null) {
5886a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[2];
5887a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        } else {
5888a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[selectionArgs.length + 2];
5889a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
5890a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5891a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[0] = String.valueOf(contactId);
5892a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[1] = Uri.encode(lookupKey);
5893a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        lookupQb.appendWhere(contactIdColumn + "=? AND " + lookupKeyColumn + "=?");
5894a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        Cursor c = query(db, lookupQb, projection, selection, args, sortOrder,
5895a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                groupBy, limit);
5896a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (c.getCount() != 0) {
5897a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return c;
5898a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5899a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5900a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        c.close();
5901a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return null;
5902a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
590309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5904bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov    private static final class AddressBookIndexQuery {
5905bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String LETTER = "letter";
5906bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String TITLE = "title";
5907bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String COUNT = "count";
5908ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5909bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
5910bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                LETTER, TITLE, COUNT
5911ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        };
5912ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5913bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_LETTER = 0;
5914bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_TITLE = 1;
5915bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_COUNT = 2;
5916bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
59175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // The first letter of the sort key column is what is used for the index headings.
59185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        public static final String SECTION_HEADING = "SUBSTR(%1$s,1,1)";
591924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
5920de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa        public static final String ORDER_BY = LETTER + " COLLATE " + PHONEBOOK_COLLATOR_NAME;
5921ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
5922ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5923ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    /**
5924ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * Computes counts by the address book index titles and adds the resulting tally
5925ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * to the returned cursor as a bundle of extras.
5926ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     */
5927ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    private Cursor bundleLetterCountExtras(Cursor cursor, final SQLiteDatabase db,
59282ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki            SQLiteQueryBuilder qb, String selection, String[] selectionArgs, String sortOrder,
59292ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki            String countExpression) {
5930409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki        if (!(cursor instanceof AbstractCursor)) {
5931409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            Log.w(TAG, "Unable to bundle extras.  Cursor is not AbstractCursor.");
5932409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            return cursor;
5933409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki        }
5934ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortKey;
5935ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5936ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // The sort order suffix could be something like "DESC".
5937ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // We want to preserve it in the query even though we will change
5938ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // the sort column itself.
5939ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortOrderSuffix = "";
5940ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (sortOrder != null) {
5941ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int spaceIndex = sortOrder.indexOf(' ');
5942ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            if (spaceIndex != -1) {
5943ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder.substring(0, spaceIndex);
5944ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortOrderSuffix = sortOrder.substring(spaceIndex);
5945ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            } else {
5946ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder;
5947ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
5948ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } else {
5949ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            sortKey = Contacts.SORT_KEY_PRIMARY;
5950ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5951ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5952bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        String locale = getLocale().toString();
5953ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        HashMap<String, String> projectionMap = Maps.newHashMap();
59545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        String sectionHeading = String.format(AddressBookIndexQuery.SECTION_HEADING, sortKey);
5955bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.LETTER,
595624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                sectionHeading + " AS " + AddressBookIndexQuery.LETTER);
5957bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
59582ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        // If "what to count" is not specified, we just count all records.
59592ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        if (TextUtils.isEmpty(countExpression)) {
59602ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki            countExpression = "*";
59612ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        }
59622ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki
5963bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        /**
5964bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * Use the GET_PHONEBOOK_INDEX function, which is an android extension for SQLite3,
5965bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * to map the first letter of the sort key to a character that is traditionally
5966bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * used in phonebooks to represent that letter.  For example, in Korean it will
5967bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * be the first consonant in the letter; for Japanese it will be Hiragana rather
5968bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * than Katakana.
5969bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         */
5970ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.TITLE,
597124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                "GET_PHONEBOOK_INDEX(" + sectionHeading + ",'" + locale + "')"
5972bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                        + " AS " + AddressBookIndexQuery.TITLE);
5973ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.COUNT,
59742ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                "COUNT(" + countExpression + ") AS " + AddressBookIndexQuery.COUNT);
5975ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        qb.setProjectionMap(projectionMap);
5976ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5977f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov        Cursor indexCursor = qb.query(db, AddressBookIndexQuery.COLUMNS, selection, selectionArgs,
5978ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY, null /* having */,
5979ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY + sortOrderSuffix);
5980ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5981ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        try {
5982f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            int groupCount = indexCursor.getCount();
5983ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            String titles[] = new String[groupCount];
5984ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int counts[] = new int[groupCount];
5985bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            int indexCount = 0;
5986bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            String currentTitle = null;
5987bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5988bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // Since GET_PHONEBOOK_INDEX is a many-to-1 function, we may end up
5989bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // with multiple entries for the same title.  The following code
5990bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // collapses those duplicates.
5991ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            for (int i = 0; i < groupCount; i++) {
5992f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov                indexCursor.moveToNext();
5993bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String title = indexCursor.getString(AddressBookIndexQuery.COLUMN_TITLE);
5994bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int count = indexCursor.getInt(AddressBookIndexQuery.COLUMN_COUNT);
5995bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                if (indexCount == 0 || !TextUtils.equals(title, currentTitle)) {
5996bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    titles[indexCount] = currentTitle = title;
5997bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount] = count;
5998bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    indexCount++;
5999bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                } else {
6000bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount - 1] += count;
6001bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                }
6002bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            }
6003bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
6004bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            if (indexCount < groupCount) {
6005bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String[] newTitles = new String[indexCount];
6006bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(titles, 0, newTitles, 0, indexCount);
6007bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                titles = newTitles;
6008bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
6009bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int[] newCounts = new int[indexCount];
6010bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(counts, 0, newCounts, 0, indexCount);
6011bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                counts = newCounts;
6012ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
6013ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6014409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            final Bundle bundle = new Bundle();
6015409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            bundle.putStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES, titles);
6016409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            bundle.putIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS, counts);
6017409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki
6018409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            ((AbstractCursor) cursor).setExtras(bundle);
6019409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            return cursor;
6020ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } finally {
6021f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            indexCursor.close();
6022ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
6023ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
6024ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
60252d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    /**
602692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Returns the contact Id for the contact identified by the lookupKey.
602792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Robust against changes in the lookup key: if the key has changed, will
602892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * look up the contact by the raw contact IDs or name encoded in the lookup
602992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * key.
60302d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     */
60312d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    public long lookupContactIdByLookupKey(SQLiteDatabase db, String lookupKey) {
60325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactLookupKey key = new ContactLookupKey();
60335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ArrayList<LookupKeySegment> segments = key.parse(lookupKey);
60345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
603592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        long contactId = -1;
60365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_PROFILE)) {
60375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            // We should already be in a profile database context, so just look up a single contact.
60385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro           contactId = lookupSingleContactId(db);
60395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
60405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
604192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_SOURCE_ID)) {
604292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdBySourceIds(db, segments);
604392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
604492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
604592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
604692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
604792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
604892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        boolean hasRawContactIds =
604992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID);
605092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds) {
605192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdByRawContactIds(db, segments);
605292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
605392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
605492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
605592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
605692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
605792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds
605892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                || lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME)) {
60595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            contactId = lookupContactIdByDisplayNames(db, segments);
60605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
60615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
60625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return contactId;
60635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
60645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
60655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private long lookupSingleContactId(SQLiteDatabase db) {
60665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = db.query(Tables.CONTACTS, new String[] {Contacts._ID},
60675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                null, null, null, null, null, "1");
60685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        try {
60695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (c.moveToFirst()) {
60705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return c.getLong(0);
60715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            } else {
60725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return -1;
60735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
60745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } finally {
60755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            c.close();
60765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
60775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
60785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
60795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupBySourceIdQuery {
608043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String TABLE = Views.RAW_CONTACTS;
60815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
60825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
60835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
608443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
60855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
60865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.SOURCE_ID
60875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
60885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
60895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
609043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int ACCOUNT_TYPE_AND_DATA_SET = 1;
60915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
60925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int SOURCE_ID = 3;
60935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
60945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
60955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdBySourceIds(SQLiteDatabase db,
60965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
60975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
60985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(RawContacts.SOURCE_ID + " IN (");
60995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
61005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
610192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID) {
61025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
61035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
61045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
61055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
61065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
61075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
61085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
61095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupBySourceIdQuery.TABLE, LookupBySourceIdQuery.COLUMNS,
61105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
61115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
61125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
611343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                String accountTypeAndDataSet =
611443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE_AND_DATA_SET);
61155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME);
61165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
611743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        ContactLookupKey.getAccountHashCode(accountTypeAndDataSet, accountName);
61185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID);
61195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
61205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
612192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID
612292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
61235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(sourceId)) {
61245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupBySourceIdQuery.CONTACT_ID);
61255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
61265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
61275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
61285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
61295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
61305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
61315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
61325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
61335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
61345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
61355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
613692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByRawContactIdQuery {
613743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String TABLE = Views.RAW_CONTACTS;
61385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
61395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
61405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
614143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
61425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
614392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts._ID,
61445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
61455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
61465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
614743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int ACCOUNT_TYPE_AND_DATA_SET = 1;
61485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
614992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ID = 3;
61505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
61515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
615292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByRawContactIds(SQLiteDatabase db,
615392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            ArrayList<LookupKeySegment> segments) {
615492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
615592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(RawContacts._ID + " IN (");
61565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
61575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
615892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
615992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(segment.rawContactId);
616092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(",");
61615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
61625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
616392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
616492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
61655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
616692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        Cursor c = db.query(LookupByRawContactIdQuery.TABLE, LookupByRawContactIdQuery.COLUMNS,
616792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                 sb.toString(), null, null, null, null);
616892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        try {
616992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            while (c.moveToNext()) {
617043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                String accountTypeAndDataSet = c.getString(
617143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        LookupByRawContactIdQuery.ACCOUNT_TYPE_AND_DATA_SET);
617292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountName = c.getString(LookupByRawContactIdQuery.ACCOUNT_NAME);
617392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                int accountHashCode =
617443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        ContactLookupKey.getAccountHashCode(accountTypeAndDataSet, accountName);
617592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String rawContactId = c.getString(LookupByRawContactIdQuery.ID);
617692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
617792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
617892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID
617992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
618092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && segment.rawContactId.equals(rawContactId)) {
618192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        segment.contactId = c.getLong(LookupByRawContactIdQuery.CONTACT_ID);
618292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        break;
618392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    }
618492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                }
618592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
618692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        } finally {
618792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            c.close();
61885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
61895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
619092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return getMostReferencedContactId(segments);
619192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
619292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
619392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByDisplayNameQuery {
619492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
619592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
619692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String COLUMNS[] = {
619792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.CONTACT_ID,
619843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
619992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
620092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME
620192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        };
620292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
620392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int CONTACT_ID = 0;
620443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int ACCOUNT_TYPE_AND_DATA_SET = 1;
620592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_NAME = 2;
620692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int NORMALIZED_NAME = 3;
620792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
620892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
620992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByDisplayNames(SQLiteDatabase db,
621092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
62115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
62125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(NameLookupColumns.NORMALIZED_NAME + " IN (");
62135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
62145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
621592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
621692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
62175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
62185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
62195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
62205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
62215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
62225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
62235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                + " AND " + RawContacts.CONTACT_ID + " NOT NULL");
62245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupByDisplayNameQuery.TABLE, LookupByDisplayNameQuery.COLUMNS,
62265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
62275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
62285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
622943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                String accountTypeAndDataSet =
623043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE_AND_DATA_SET);
62315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME);
62325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
623343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        ContactLookupKey.getAccountHashCode(accountTypeAndDataSet, accountName);
62345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME);
62355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
62365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
623792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if ((segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
623892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID)
623992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
62405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(name)) {
62415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupByDisplayNameQuery.CONTACT_ID);
62425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
62435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
62445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
62455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
62465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
62475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
62485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
62495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
62515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
62525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
625392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private boolean lookupKeyContainsType(ArrayList<LookupKeySegment> segments, int lookupType) {
625492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
625592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            LookupKeySegment segment = segments.get(i);
625692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == lookupType) {
625792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return true;
625892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
625992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
626092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
626192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return false;
626292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
626392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
6264ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateLookupKeyForRawContact(SQLiteDatabase db, long rawContactId) {
62655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().updateLookupKeyForRawContact(db, rawContactId);
6266ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    }
6267ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov
62685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /**
62695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     * Returns the contact ID that is mentioned the highest number of times.
62705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     */
62715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long getMostReferencedContactId(ArrayList<LookupKeySegment> segments) {
62725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Collections.sort(segments);
62735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long bestContactId = -1;
62755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int bestRefCount = 0;
62765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = -1;
62785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int count = 0;
62795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int segmentCount = segments.size();
62815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segmentCount; i++) {
62825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
62835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.contactId != -1) {
62845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segment.contactId == contactId) {
62855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count++;
62865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                } else {
62875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (count > bestRefCount) {
62885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestContactId = contactId;
62895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestRefCount = count;
62905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
62915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    contactId = segment.contactId;
62925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count = 1;
62935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
62945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
62955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
62965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count > bestRefCount) {
62975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return contactId;
62985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
62995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return bestContactId;
63005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
63015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
63025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
6303763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
6304763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            String[] projection) {
63054928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa        setTablesAndProjectionMapForContacts(qb, uri, projection, false);
63062f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    }
63072f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
63082f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    /**
63094928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * @param includeDataUsageStat true when the table should include DataUsageStat table.
63104928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * Note that this uses INNER JOIN instead of LEFT OUTER JOIN, so some of data in Contacts
63114928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * may be dropped.
63122f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     */
63132f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
63144928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            String[] projection, boolean includeDataUsageStat) {
631582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
6316ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.CONTACTS);
63172f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
63182f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa        // Just for frequently contacted contacts in Strequent Uri handling.
63194928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa        if (includeDataUsageStat) {
63202f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            sb.append(" INNER JOIN " +
6321ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                    Views.DATA_USAGE_STAT + " AS " + Tables.DATA_USAGE_STAT +
63222f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    " ON (" +
63232f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    DbQueryUtils.concatenateClauses(
63242f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                            DataUsageStatColumns.CONCRETE_TIMES_USED + " > 0",
63254928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            RawContacts.CONTACT_ID + "=" + Views.CONTACTS + "." + Contacts._ID) +
63262f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    ")");
63272f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa        }
63282f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
63297ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
63307ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
6331916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setTables(sb.toString());
6332916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionMap);
6333916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    }
6334916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
6335916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /**
6336916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * Finds name lookup records matching the supplied filter, picks one arbitrary match per
6337916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * contact and joins that with other contacts tables.
6338916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     */
6339916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private void setTablesAndProjectionMapForContactsWithSnippet(SQLiteQueryBuilder qb, Uri uri,
6340b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            String[] projection, String filter, long directoryId, boolean deferredSnippeting) {
63417ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov
63427ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
6343ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.CONTACTS);
6344916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
634503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        if (filter != null) {
634603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            filter = filter.trim();
634703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        }
634803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
634930cc766756461da8d53933f88ea01dd2272a90ebDmitri Plotnikov        if (TextUtils.isEmpty(filter) || (directoryId != -1 && directoryId != Directory.DEFAULT)) {
635030cc766756461da8d53933f88ea01dd2272a90ebDmitri Plotnikov            sb.append(" JOIN (SELECT NULL AS " + SearchSnippetColumns.SNIPPET + " WHERE 0)");
63515e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        } else {
6352b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            appendSearchIndexJoin(sb, uri, projection, filter, deferredSnippeting);
63535e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        }
63547ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
63557ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
635603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        qb.setTables(sb.toString());
635703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionWithSnippetMap);
635803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    }
6359916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
636003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private void appendSearchIndexJoin(
6361b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            StringBuilder sb, Uri uri, String[] projection, String filter,
6362b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            boolean  deferredSnippeting) {
6363916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
6364b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        if (snippetNeeded(projection)) {
636503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String[] args = null;
636603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String snippetArgs =
636703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                    getQueryParameter(uri, SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY);
636803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            if (snippetArgs != null) {
636903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                args = snippetArgs.split(",");
637003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
637103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
63725e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String startMatch = args != null && args.length > 0 ? args[0]
63735e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_START_MATCH;
63745e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String endMatch = args != null && args.length > 1 ? args[1]
63755e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_END_MATCH;
63765e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String ellipsis = args != null && args.length > 2 ? args[2]
63775e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_ELLIPSIS;
63785e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            int maxTokens = args != null && args.length > 3 ? Integer.parseInt(args[3])
63795e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_MAX_TOKENS;
63805e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
6381174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            appendSearchIndexJoin(
6382b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                    sb, filter, true, startMatch, endMatch, ellipsis, maxTokens,
6383b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                    deferredSnippeting);
6384174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        } else {
6385b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            appendSearchIndexJoin(sb, filter, false, null, null, null, 0, false);
6386174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
6387174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    }
6388174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
6389174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    public void appendSearchIndexJoin(StringBuilder sb, String filter,
6390174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            boolean snippetNeeded, String startMatch, String endMatch, String ellipsis,
6391b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            int maxTokens, boolean deferredSnippeting) {
6392174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        boolean isEmailAddress = false;
6393174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String emailAddress = null;
6394174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        boolean isPhoneNumber = false;
6395174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String phoneNumber = null;
6396174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String numberE164 = null;
6397174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
63983716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // If the query consists of a single word, we can do snippetizing after-the-fact for a
63993716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // performance boost.
6400b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        boolean singleTokenSearch = isSingleWordQuery(filter);
64013716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
6402174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (filter.indexOf('@') != -1) {
64035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            emailAddress = mDbHelper.get().extractAddressFromEmailAddress(filter);
6404174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            isEmailAddress = !TextUtils.isEmpty(emailAddress);
6405174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        } else {
6406174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            isPhoneNumber = isPhoneNumber(filter);
640704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            if (isPhoneNumber) {
640804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                phoneNumber = PhoneNumberUtils.normalizeNumber(filter);
640904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                numberE164 = PhoneNumberUtils.formatNumberToE164(phoneNumber,
64105d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mDbHelper.get().getCountryIso());
641104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            }
6412174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
6413174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
6414174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        sb.append(" JOIN (SELECT " + SearchIndexColumns.CONTACT_ID + " AS snippet_contact_id");
6415174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (snippetNeeded) {
64165e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            sb.append(", ");
64175e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            if (isEmailAddress) {
64183d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append("ifnull(");
64195e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, startMatch);
642004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("||(SELECT MIN(" + Email.ADDRESS + ")");
642104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" FROM " + Tables.DATA_JOIN_RAW_CONTACTS);
642204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" WHERE  " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
642304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + RawContacts.CONTACT_ID + " AND " + Email.ADDRESS + " LIKE ");
642404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                DatabaseUtils.appendEscapedSQLString(sb, filter + "%");
642504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(")||");
64263d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, endMatch);
64273d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append(",");
64283716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
6429b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                // Optimization for single-token search (do only if requested).
6430b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                if (singleTokenSearch && deferredSnippeting) {
64313716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    sb.append(SearchIndexColumns.CONTENT);
64323716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                } else {
64333716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
64343716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                }
64353d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append(")");
64363d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov            } else if (isPhoneNumber) {
64373d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append("ifnull(");
64383d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, startMatch);
643904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("||(SELECT MIN(" + Phone.NUMBER + ")");
644004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" FROM " +
644104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                        Tables.DATA_JOIN_RAW_CONTACTS + " JOIN " + Tables.PHONE_LOOKUP);
644204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" ON " + DataColumns.CONCRETE_ID);
644304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + Tables.PHONE_LOOKUP + "." + PhoneLookupColumns.DATA_ID);
644404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" WHERE  " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
644504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + RawContacts.CONTACT_ID);
644604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" AND " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
644704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(phoneNumber);
644804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("%'");
644904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                if (!TextUtils.isEmpty(numberE164)) {
645004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append(" OR " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
645104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append(numberE164);
645204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append("%'");
645304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                }
645404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(")||");
64555e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, endMatch);
64565e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                sb.append(",");
64573716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
6458b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                // Optimization for single-token search (do only if requested).
6459b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                if (singleTokenSearch && deferredSnippeting) {
64603716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    sb.append(SearchIndexColumns.CONTENT);
64613716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                } else {
64623716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
64633716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                }
64645e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                sb.append(")");
646503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            } else {
646604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                final String normalizedFilter = NameNormalizer.normalize(filter);
646704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                if (!TextUtils.isEmpty(normalizedFilter)) {
6468b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                    // Optimization for single-token search (do only if requested)..
6469b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                    if (singleTokenSearch && deferredSnippeting) {
64703716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(SearchIndexColumns.CONTENT);
64713716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    } else {
64723716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("(CASE WHEN EXISTS (SELECT 1 FROM ");
64733716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.RAW_CONTACTS + " AS rc INNER JOIN ");
64743716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.NAME_LOOKUP + " AS nl ON (rc." + RawContacts._ID);
64753716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("=nl." + NameLookupColumns.RAW_CONTACT_ID);
64763716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(") WHERE nl." + NameLookupColumns.NORMALIZED_NAME);
64773716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(" GLOB '" + normalizedFilter + "*' AND ");
64783716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("nl." + NameLookupColumns.NAME_TYPE + "=");
64793716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(NameLookupType.NAME_COLLATION_KEY + " AND ");
64803716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
64813716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("=rc." + RawContacts.CONTACT_ID);
64823716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(") THEN NULL ELSE ");
64833716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
64843716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(" END)");
64853716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    }
648604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                } else {
648704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append("NULL");
648804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                }
648903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
64905e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            sb.append(" AS " + SearchSnippetColumns.SNIPPET);
64915e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        }
649203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
64935e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(" FROM " + Tables.SEARCH_INDEX);
64945e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(" WHERE ");
64955e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(Tables.SEARCH_INDEX + " MATCH ");
64965e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        if (isEmailAddress) {
64972352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb, "\"" + sanitizeMatch(filter) + "*\"");
64983d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov        } else if (isPhoneNumber) {
64992352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb,
650004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    "\"" + sanitizeMatch(filter) + "*\" OR \"" + phoneNumber + "*\""
65012352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                            + (numberE164 != null ? " OR \"" + numberE164 + "\"" : ""));
650203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        } else {
65032352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filter) + "*");
65049c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        }
650503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        sb.append(") ON (" + Contacts._ID + "=snippet_contact_id)");
6506a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov    }
6507a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov
65082352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov    private String sanitizeMatch(String filter) {
65092352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov        // TODO more robust preprocessing of match expressions
65102352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov        return filter.replace('-', ' ').replace('\"', ' ');
65112352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov    }
65122352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov
65135e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov    private void appendSnippetFunction(
65145e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            StringBuilder sb, String startMatch, String endMatch, String ellipsis, int maxTokens) {
65155e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append("snippet(" + Tables.SEARCH_INDEX + ",");
65165e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, startMatch);
65175e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",");
65185e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, endMatch);
65195e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",");
65205e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, ellipsis);
65215e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
65225e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        // The index of the column used for the snippet, "content"
65235e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",1,");
65245e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(maxTokens);
65255e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(")");
65265e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov    }
65275e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
6528763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForRawContacts(SQLiteQueryBuilder qb, Uri uri) {
6529763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        StringBuilder sb = new StringBuilder();
6530ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.RAW_CONTACTS);
6531763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setTables(sb.toString());
6532763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setProjectionMap(sRawContactsProjectionMap);
6533f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        appendAccountFromParameter(qb, uri);
6534763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    }
6535763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
6536a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForRawEntities(SQLiteQueryBuilder qb, Uri uri) {
6537ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        qb.setTables(Views.RAW_ENTITIES);
6538a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sRawEntityProjectionMap);
6539f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        appendAccountFromParameter(qb, uri);
654046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    }
654146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
654282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
654382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String[] projection, boolean distinct) {
654446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        setTablesAndProjectionMapForData(qb, uri, projection, distinct, null);
654546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
654646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
654746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
654846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * @param usageType when non-null {@link Tables#DATA_USAGE_STAT} is joined with the specified
654946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * type.
655046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
655146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
655246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            String[] projection, boolean distinct, Integer usageType) {
655382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
6554ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.DATA);
655582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sb.append(" data");
655682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
6557a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, RawContacts.CONTACT_ID);
6558a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
6559a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
6560a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
65613296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
656246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (usageType != null) {
656346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            appendDataUsageStatJoin(sb, usageType, DataColumns.CONCRETE_ID);
656446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
656546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
656682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
6567f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov
6568f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        boolean useDistinct = distinct
65695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                || !mDbHelper.get().isInProjection(projection, DISTINCT_DATA_PROHIBITING_COLUMNS);
6570f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setDistinct(useDistinct);
6571f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setProjectionMap(useDistinct ? sDistinctDataProjectionMap : sDataProjectionMap);
6572f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        appendAccountFromParameter(qb, uri);
6573ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    }
6574ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
65750a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb,
65760a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String[] projection) {
65770a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        StringBuilder sb = new StringBuilder();
6578ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.DATA);
65790a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        sb.append(" data");
6580a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
6581a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
65820a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
6583a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
6584a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sStatusUpdatesProjectionMap);
6585a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6586a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
65873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void setTablesAndProjectionMapForStreamItems(SQLiteQueryBuilder qb) {
65889b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann        qb.setTables(Views.STREAM_ITEMS);
65893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        qb.setProjectionMap(sStreamItemsProjectionMap);
65903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
65913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
65923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void setTablesAndProjectionMapForStreamItemPhotos(SQLiteQueryBuilder qb) {
65931dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro        qb.setTables(Tables.PHOTO_FILES
65941dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + " JOIN " + Tables.STREAM_ITEM_PHOTOS + " ON ("
65951dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + StreamItemPhotosColumns.CONCRETE_PHOTO_FILE_ID + "="
65961dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + PhotoFilesColumns.CONCRETE_ID
65971dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + ") JOIN " + Tables.STREAM_ITEMS + " ON ("
65981dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "="
65990bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                + StreamItemsColumns.CONCRETE_ID + ")"
66000bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                + " JOIN " + Tables.RAW_CONTACTS + " ON ("
66010bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                + StreamItemsColumns.CONCRETE_RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID
66020bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                + ")");
66033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        qb.setProjectionMap(sStreamItemPhotosProjectionMap);
66043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
66053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
6606a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForEntities(SQLiteQueryBuilder qb, Uri uri,
6607a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection) {
6608a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
6609ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.ENTITIES);
6610a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(" data");
6611a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6612a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts.Entity.CONTACT_ID);
6613a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
6614a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, Contacts.Entity.DATA_ID);
6615a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, Contacts.Entity.DATA_ID);
6616a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6617a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
6618a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sEntityProjectionMap);
6619f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        appendAccountFromParameter(qb, uri);
6620a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6621a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6622a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactStatusUpdateJoin(StringBuilder sb, String[] projection,
6623a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String lastStatusUpdateIdColumn) {
66245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mDbHelper.get().isInProjection(projection,
6625a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS,
6626a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
6627a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
6628a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
6629a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP)) {
6630a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
6631a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    + ContactsStatusUpdatesColumns.ALIAS +
6632a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + lastStatusUpdateIdColumn + "="
6633a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
66340a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
6635a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
66360a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
6637a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataStatusUpdateJoin(StringBuilder sb, String[] projection,
6638a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
66395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mDbHelper.get().isInProjection(projection,
66400a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS,
66410a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_RES_PACKAGE,
66420a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_ICON,
66430a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_LABEL,
66440a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_TIMESTAMP)) {
66450a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
6646a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdatesColumns.CONCRETE_DATA_ID + "="
6647a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + dataIdColumn + ")");
66480a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
6649a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6650a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
665146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private void appendDataUsageStatJoin(StringBuilder sb, int usageType, String dataIdColumn) {
665246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        sb.append(" LEFT OUTER JOIN " + Tables.DATA_USAGE_STAT +
665346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "=" + dataIdColumn +
665446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                " AND " + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "=" + usageType + ")");
665546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
665646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
6657a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactPresenceJoin(StringBuilder sb, String[] projection,
6658a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn) {
66595d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mDbHelper.get().isInProjection(projection,
6660a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_PRESENCE, Contacts.CONTACT_CHAT_CAPABILITY)) {
6661a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
6662a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + contactIdColumn + " = "
6663a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + AggregatedPresenceColumns.CONCRETE_CONTACT_ID + ")");
6664a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
6665a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6666a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6667a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataPresenceJoin(StringBuilder sb, String[] projection,
6668a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
66695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mDbHelper.get().isInProjection(projection, Data.PRESENCE, Data.CHAT_CAPABILITY)) {
6670a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
6671a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdates.DATA_ID + "=" + dataIdColumn + ")");
6672a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
6673a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6674a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
667524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private boolean appendLocalDirectorySelectionIfNeeded(SQLiteQueryBuilder qb, long directoryId) {
6676385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directoryId == Directory.DEFAULT) {
6677385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " IN " + Tables.DEFAULT_DIRECTORY);
667824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            return true;
6679385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directoryId == Directory.LOCAL_INVISIBLE){
6680385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " NOT IN " + Tables.DEFAULT_DIRECTORY);
668124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            return true;
668224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
668324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        return false;
668424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
668524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
6686f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro    private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) {
6687f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
6688f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
668943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        final String dataSet = getQueryParameter(uri, RawContacts.DATA_SET);
6690e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6691e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
6692e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
6693e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
66945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
6695fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
6696e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
6697e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6698e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
6699e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
6700e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
6701e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
670243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            String toAppend = RawContacts.ACCOUNT_NAME + "="
67034a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
67044a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
670543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + DatabaseUtils.sqlEscapeString(accountType);
6706f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            if (dataSet == null) {
6707f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                toAppend += " AND " + RawContacts.DATA_SET + " IS NULL";
6708f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            } else {
6709f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                toAppend += " AND " + RawContacts.DATA_SET + "=" +
6710f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                        DatabaseUtils.sqlEscapeString(dataSet);
671143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
671243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            qb.appendWhere(toAppend);
67134a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        } else {
67144a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere("1");
67154a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
67164a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
67174a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
6718e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    private String appendAccountToSelection(Uri uri, String selection) {
6719f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
6720f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
672143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        final String dataSet = getQueryParameter(uri, RawContacts.DATA_SET);
6722e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6723e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
6724e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
6725e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
67265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
6727fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
6728e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
6729e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6730e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
6731e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
6732e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
6733e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
6734e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "="
6735e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
6736e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + RawContacts.ACCOUNT_TYPE + "="
6737e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountType));
6738f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            if (dataSet == null) {
6739f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                selectionSb.append(" AND " + RawContacts.DATA_SET + " IS NULL");
6740f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            } else {
674143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                selectionSb.append(" AND " + RawContacts.DATA_SET + "=")
674243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        .append(DatabaseUtils.sqlEscapeString(dataSet));
674343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
6744e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (!TextUtils.isEmpty(selection)) {
6745e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(" AND (");
6746e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(selection);
6747e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(')');
6748e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
6749e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selectionSb.toString();
6750e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } else {
6751e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selection;
6752e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
6753e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    }
6754e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
67557e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
6756c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
6757c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *
6758c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
6759c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
6760c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     */
6761f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private String getLimit(Uri uri) {
67622e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limitParam = getQueryParameter(uri, ContactsContract.LIMIT_PARAM_KEY);
6763c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limitParam == null) {
6764c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
6765c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
6766c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // make sure that the limit is a non-negative integer
6767c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        try {
6768c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int l = Integer.parseInt(limitParam);
6769c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (l < 0) {
6770c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
6771c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return null;
6772c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
6773c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return String.valueOf(l);
6774c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        } catch (NumberFormatException ex) {
6775c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
6776c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
6777c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
6778c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
6779c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
6780b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    @Override
6781f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
6782f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (mode.equals("r")) {
6783f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            waitForAccess(mReadAccessLatch);
6784f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } else {
6785f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            waitForAccess(mWriteAccessLatch);
6786f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
67875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDb(uri)) {
67885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToProfileMode();
67895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return mProfileProvider.openAssetFile(uri, mode);
67905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } else {
67915d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToContactMode();
67925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (mode.equals("r")) {
67935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mDb = mDbHelper.get().getReadableDatabase();
67945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            } else {
67955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mDb = mDbHelper.get().getWritableDatabase();
67965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
67975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return openAssetFileLocal(uri, mode);
67985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
67995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
68005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
68015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    public AssetFileDescriptor openAssetFileLocal(Uri uri, String mode)
68025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            throws FileNotFoundException {
68035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
68045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
68055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
68065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.set(mDb);
68075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
6808415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
6809b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        int match = sUriMatcher.match(uri);
6810b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        switch (match) {
6811a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
6812bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                long contactId = Long.parseLong(uri.getPathSegments().get(1));
68135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return openPhotoAssetFile(mActiveDb.get(), uri, mode,
681424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        Data._ID + "=" + Contacts.PHOTO_ID + " AND " +
681524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                                RawContacts.CONTACT_ID + "=?",
6816bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        new String[]{String.valueOf(contactId)});
6817e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            }
6818b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
6819f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_ID_DISPLAY_PHOTO: {
6820f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
6821f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
6822f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Display photos retrieved by contact ID can only be read.");
6823f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6824f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long contactId = Long.parseLong(uri.getPathSegments().get(1));
68255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = mActiveDb.get().query(Tables.CONTACTS,
6826f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{Contacts.PHOTO_FILE_ID},
6827f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        Contacts._ID + "=?", new String[]{String.valueOf(contactId)},
6828f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        null, null, null);
6829f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
6830f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.moveToFirst();
6831f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    long photoFileId = c.getLong(0);
6832f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForRead(photoFileId);
6833f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
6834f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
6835f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6836f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6837f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6838bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_PHOTO:
6839bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_ID_PHOTO:
6840f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_DISPLAY_PHOTO:
6841f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_ID_DISPLAY_PHOTO: {
6842f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
6843f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
6844bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                            "Photos retrieved by contact lookup key can only be read.");
6845f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6846f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                List<String> pathSegments = uri.getPathSegments();
6847f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                int segmentCount = pathSegments.size();
6848f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (segmentCount < 4) {
68495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
6850f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Missing a lookup key", uri));
6851f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6852bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro
6853bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                boolean forDisplayPhoto = (match == CONTACTS_LOOKUP_ID_DISPLAY_PHOTO
6854bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        || match == CONTACTS_LOOKUP_DISPLAY_PHOTO);
6855f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                String lookupKey = pathSegments.get(2);
6856bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                String[] projection = new String[]{Contacts.PHOTO_ID, Contacts.PHOTO_FILE_ID};
6857f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (segmentCount == 5) {
6858f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    long contactId = Long.parseLong(pathSegments.get(3));
6859f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
6860f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
68615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
6862f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            projection, null, null, null, null, null,
6863f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
6864f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (c != null) {
6865f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        try {
6866f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            c.moveToFirst();
6867bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                            if (forDisplayPhoto) {
6868bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                long photoFileId =
6869bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                        c.getLong(c.getColumnIndex(Contacts.PHOTO_FILE_ID));
6870bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                return openDisplayPhotoForRead(photoFileId);
6871bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                            } else {
6872bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                long photoId = c.getLong(c.getColumnIndex(Contacts.PHOTO_ID));
6873bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                return openPhotoAssetFile(mActiveDb.get(), uri, mode,
6874bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                        Data._ID + "=?", new String[]{String.valueOf(photoId)});
6875bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                            }
6876f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        } finally {
6877f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            c.close();
6878f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
6879f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
6880f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6881f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6882f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
6883f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                setTablesAndProjectionMapForContacts(qb, uri, projection);
68845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
68855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = qb.query(mActiveDb.get(), projection, Contacts._ID + "=?",
6886f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{String.valueOf(contactId)}, null, null, null);
6887f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
6888f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.moveToFirst();
6889bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    if (forDisplayPhoto) {
6890bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        long photoFileId = c.getLong(c.getColumnIndex(Contacts.PHOTO_FILE_ID));
6891bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        return openDisplayPhotoForRead(photoFileId);
6892bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    } else {
6893bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        long photoId = c.getLong(c.getColumnIndex(Contacts.PHOTO_ID));
6894bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        return openPhotoAssetFile(mActiveDb.get(), uri, mode,
6895bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                Data._ID + "=?", new String[]{String.valueOf(photoId)});
6896bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    }
6897f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
6898f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
6899f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6900f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6901f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6902f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case RAW_CONTACTS_ID_DISPLAY_PHOTO: {
6903f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
6904f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                boolean writeable = !mode.equals("r");
6905f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6906f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Find the primary photo data record for this raw contact.
6907f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
6908f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                String[] projection = new String[]{Data._ID, Photo.PHOTO_FILE_ID};
6909f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
69107cf50494501938f175d288077145acf49da8f171Daniel Lehmann                long photoMimetypeId = mDbHelper.get().getMimeTypeId(Photo.CONTENT_ITEM_TYPE);
69115d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = qb.query(mActiveDb.get(), projection,
69127cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        Data.RAW_CONTACT_ID + "=? AND " + DataColumns.MIMETYPE_ID + "=?",
69137cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        new String[]{String.valueOf(rawContactId), String.valueOf(photoMimetypeId)},
6914f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        null, null, Data.IS_PRIMARY + " DESC");
6915f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long dataId = 0;
6916f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long photoFileId = 0;
6917f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
6918f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (c.getCount() >= 1) {
6919f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        c.moveToFirst();
6920f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        dataId = c.getLong(0);
6921f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        photoFileId = c.getLong(1);
6922f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
6923f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
6924f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
6925f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6926f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6927f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // If writeable, open a writeable file descriptor that we can monitor.
6928f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // When the caller finishes writing content, we'll process the photo and
6929f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // update the data record.
6930f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (writeable) {
6931f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForWrite(rawContactId, dataId, uri, mode);
6932f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } else {
6933f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForRead(photoFileId);
6934f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6935f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6936f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6937f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case DISPLAY_PHOTO: {
6938f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long photoFileId = ContentUris.parseId(uri);
6939f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
6940f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
6941f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Display photos retrieved by key can only be read.");
6942f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6943f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                return openDisplayPhotoForRead(photoFileId);
6944f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6945f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6946e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            case DATA_ID: {
694724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long dataId = Long.parseLong(uri.getPathSegments().get(1));
69487cf50494501938f175d288077145acf49da8f171Daniel Lehmann                long photoMimetypeId = mDbHelper.get().getMimeTypeId(Photo.CONTENT_ITEM_TYPE);
69495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return openPhotoAssetFile(mActiveDb.get(), uri, mode,
69507cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        Data._ID + "=? AND " + DataColumns.MIMETYPE_ID + "=" + photoMimetypeId,
695124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        new String[]{String.valueOf(dataId)});
6952d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
6953d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6954fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            case PROFILE_AS_VCARD: {
6955fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // When opening a contact as file, we pass back contents as a
6956fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // vCard-encoded stream. We build into a local buffer first,
6957fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // then pipe into MemoryFile once the exact size is known.
6958fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
6959fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(uri, localStream, null, null);
6960fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                return buildAssetFileDescriptor(localStream);
6961fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            }
696242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
6963fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            case CONTACTS_AS_VCARD: {
696442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // When opening a contact as file, we pass back contents as a
696542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // vCard-encoded stream. We build into a local buffer first,
696642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // then pipe into MemoryFile once the exact size is known.
696742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
6968fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(uri, localStream, null, null);
6969f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
697042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
697142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
697242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
697342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKeys = uri.getPathSegments().get(2);
697442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String[] loopupKeyList = lookupKeys.split(":");
697542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final StringBuilder inBuilder = new StringBuilder();
6976fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                Uri queryUri = Contacts.CONTENT_URI;
697742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                int index = 0;
6978fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen
6979d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // SQLite has limits on how many parameters can be used
6980d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // so the IDs are concatenated to a query string here instead
698142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                for (String lookupKey : loopupKeyList) {
698242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    if (index == 0) {
6983d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append("(");
698442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    } else {
6985d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append(",");
698642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    }
69875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    // TODO: Figure out what to do if the profile contact is in the list.
69885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
698924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    inBuilder.append(contactId);
699042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    index++;
699142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                }
699242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                inBuilder.append(')');
699342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + " IN " + inBuilder.toString();
6994d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6995d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // When opening a contact as file, we pass back contents as a
6996d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // vCard-encoded stream. We build into a local buffer first,
6997d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // then pipe into MemoryFile once the exact size is known.
6998d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
6999fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(queryUri, localStream, selection, null);
7000f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
7001d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
7002b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
7003b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            default:
70045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                throw new FileNotFoundException(mDbHelper.get().exceptionMessage(
70055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        "File does not exist", uri));
7006b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        }
7007b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
7008b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
7009afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro    private AssetFileDescriptor openPhotoAssetFile(SQLiteDatabase db, Uri uri, String mode,
7010afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro            String selection, String[] selectionArgs)
7011e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throws FileNotFoundException {
7012e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        if (!"r".equals(mode)) {
70135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            throw new FileNotFoundException(mDbHelper.get().exceptionMessage("Mode " + mode
7014e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                    + " not supported.", uri));
7015e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        }
7016e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
7017e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        String sql =
7018ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                "SELECT " + Photo.PHOTO + " FROM " + Views.DATA +
7019e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                " WHERE " + selection;
702008ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        try {
7021f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
7022f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    DatabaseUtils.blobFileDescriptorForQuery(db, sql, selectionArgs));
702308ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        } catch (SQLiteDoneException e) {
702408ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            // this will happen if the DB query returns no rows (i.e. contact does not exist)
702508ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            throw new FileNotFoundException(uri.toString());
702608ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        }
7027e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov    }
7028e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
7029f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
7030f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Opens a display photo from the photo store for reading.
7031f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param photoFileId The display photo file ID
7032f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @return An asset file descriptor that allows the file to be read.
7033f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @throws FileNotFoundException If no photo file for the given ID exists.
7034f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
7035f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private AssetFileDescriptor openDisplayPhotoForRead(long photoFileId)
7036f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            throws FileNotFoundException {
70375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        PhotoStore.Entry entry = mPhotoStore.get().get(photoFileId);
7038f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (entry != null) {
7039f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            return makeAssetFileDescriptor(
7040f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    ParcelFileDescriptor.open(new File(entry.path),
7041f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            ParcelFileDescriptor.MODE_READ_ONLY),
7042f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    entry.size);
7043f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } else {
7044f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
7045f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            throw new FileNotFoundException("No photo file found for ID " + photoFileId);
7046f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
7047f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
7048f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7049f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
7050f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Opens a file descriptor for a photo to be written.  When the caller completes writing
7051f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * to the file (closing the output stream), the image will be parsed out and processed.
7052f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * If processing succeeds, the given raw contact ID's primary photo record will be
7053f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * populated with the inserted image (if no primary photo record exists, the data ID can
7054f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * be left as 0, and a new data record will be inserted).
7055f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param rawContactId Raw contact ID this photo entry should be associated with.
7056f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param dataId Data ID for a photo mimetype that will be updated with the inserted
7057f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     image.  May be set to 0, in which case the inserted image will trigger creation
7058f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     of a new primary photo image data row for the raw contact.
7059f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param uri The URI being used to access this file.
7060f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param mode Read/write mode string.
7061f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @return An asset file descriptor the caller can use to write an image file for the
7062f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     raw contact.
7063f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
7064f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private AssetFileDescriptor openDisplayPhotoForWrite(long rawContactId, long dataId, Uri uri,
7065f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            String mode) {
7066f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        try {
7067c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            ParcelFileDescriptor[] pipeFds = ParcelFileDescriptor.createPipe();
7068c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            PipeMonitor pipeMonitor = new PipeMonitor(rawContactId, dataId, pipeFds[0]);
7069c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            pipeMonitor.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Object[]) null);
7070c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            return new AssetFileDescriptor(pipeFds[1], 0, AssetFileDescriptor.UNKNOWN_LENGTH);
7071f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } catch (IOException ioe) {
7072f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            Log.e(TAG, "Could not create temp image file in mode " + mode);
7073f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            return null;
7074f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
7075f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
7076f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7077f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
7078c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro     * Async task that monitors the given file descriptor (the read end of a pipe) for
7079c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro     * the writer finishing.  If the data from the pipe contains a valid image, the image
7080c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro     * is either inserted into the given raw contact or updated in the given data row.
7081f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
7082c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro    private class PipeMonitor extends AsyncTask<Object, Object, Object> {
7083c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro        private final ParcelFileDescriptor mDescriptor;
7084f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        private final long mRawContactId;
7085f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        private final long mDataId;
7086c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro        private PipeMonitor(long rawContactId, long dataId, ParcelFileDescriptor descriptor) {
7087f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            mRawContactId = rawContactId;
7088f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            mDataId = dataId;
7089c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            mDescriptor = descriptor;
7090f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
7091f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7092f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        @Override
7093c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro        protected Object doInBackground(Object... params) {
7094c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            AutoCloseInputStream is = new AutoCloseInputStream(mDescriptor);
7095f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            try {
7096c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                Bitmap b = BitmapFactory.decodeStream(is);
7097f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (b != null) {
7098fa4db3db4146a26f154ef2e89352ad70a5415b8eDaniel Lehmann                    waitForAccess(mWriteAccessLatch);
7099f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    PhotoProcessor processor = new PhotoProcessor(b, mMaxDisplayPhotoDim,
7100f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            mMaxThumbnailPhotoDim);
7101f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7102f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    // Store the compressed photo in the photo store.
71035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    PhotoStore photoStore = ContactsContract.isProfileId(mRawContactId)
71045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            ? mProfilePhotoStore
71055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            : mContactsPhotoStore;
71065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    long photoFileId = photoStore.insert(processor);
7107f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7108c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                    // Depending on whether we already had a data row to attach the photo
7109c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                    // to, do an update or insert.
7110f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (mDataId != 0) {
7111f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Update the data record with the new photo.
7112f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        ContentValues updateValues = new ContentValues();
7113f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7114f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Signal that photo processing has already been handled.
7115f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        updateValues.put(DataRowHandlerForPhoto.SKIP_PROCESSING_KEY, true);
7116f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7117f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        if (photoFileId != 0) {
7118f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            updateValues.put(Photo.PHOTO_FILE_ID, photoFileId);
7119f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
7120f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        updateValues.put(Photo.PHOTO, processor.getThumbnailPhotoBytes());
7121c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                        update(ContentUris.withAppendedId(Data.CONTENT_URI, mDataId),
7122c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                                updateValues, null, null);
7123f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    } else {
7124f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Insert a new primary data record with the photo.
7125f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        ContentValues insertValues = new ContentValues();
7126f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7127f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Signal that photo processing has already been handled.
7128f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(DataRowHandlerForPhoto.SKIP_PROCESSING_KEY, true);
7129f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7130f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
7131f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Data.IS_PRIMARY, 1);
7132f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        if (photoFileId != 0) {
7133f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            insertValues.put(Photo.PHOTO_FILE_ID, photoFileId);
7134f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
7135f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Photo.PHOTO, processor.getThumbnailPhotoBytes());
7136f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insert(RawContacts.CONTENT_URI.buildUpon()
7137f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                .appendPath(String.valueOf(mRawContactId))
7138f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                .appendPath(RawContacts.Data.CONTENT_DIRECTORY).build(),
7139f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                insertValues);
7140f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
7141c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro
7142f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7143c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            } catch (IOException e) {
7144c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                throw new RuntimeException(e);
7145f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
7146c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            return null;
7147f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
7148f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
7149f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7150d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String CONTACT_MEMORY_FILE_NAME = "contactAssetFile";
7151d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7152d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
7153f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert     * Returns an {@link AssetFileDescriptor} backed by the
7154d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * contents of the given {@link ByteArrayOutputStream}.
7155d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
7156f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor buildAssetFileDescriptor(ByteArrayOutputStream stream) {
7157d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        try {
7158d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            stream.flush();
7159d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7160d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final byte[] byteData = stream.toByteArray();
7161d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7162f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
7163f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    ParcelFileDescriptor.fromData(byteData, CONTACT_MEMORY_FILE_NAME),
7164f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    byteData.length);
7165d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        } catch (IOException e) {
7166ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            Log.w(TAG, "Problem writing stream into an ParcelFileDescriptor: " + e.toString());
7167ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            return null;
7168d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
7169d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
7170d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7171f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd) {
7172f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return makeAssetFileDescriptor(fd, AssetFileDescriptor.UNKNOWN_LENGTH);
7173f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
7174f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
7175f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd, long length) {
7176f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return fd != null ? new AssetFileDescriptor(fd, 0, length) : null;
7177f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
7178f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
7179d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
7180d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Output {@link RawContacts} matching the requested selection in the vCard
7181d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * format to the given {@link OutputStream}. This method returns silently if
7182d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * any errors encountered.
7183d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
7184fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen    private void outputRawContactsAsVCard(Uri uri, OutputStream stream,
7185fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            String selection, String[] selectionArgs) {
7186d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final Context context = this.getContext();
7187dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen        int vcardconfig = VCardConfig.VCARD_TYPE_DEFAULT;
7188fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen        if(uri.getBooleanQueryParameter(
7189fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                Contacts.QUERY_PARAMETER_VCARD_NO_PHOTO, false)) {
7190dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen            vcardconfig |= VCardConfig.FLAG_REFRAIN_IMAGE_EXPORT;
7191dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen        }
71927a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        final VCardComposer composer =
7193dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen                new VCardComposer(context, vcardconfig, false);
7194108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        Writer writer = null;
7195108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        try {
7196108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            writer = new BufferedWriter(new OutputStreamWriter(stream));
7197fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            if (!composer.init(uri, selection, selectionArgs, null)) {
7198108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                Log.w(TAG, "Failed to init VCardComposer");
7199108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                return;
7200108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            }
7201d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7202108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            while (!composer.isAfterLast()) {
7203108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                writer.write(composer.createOneEntry());
7204108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            }
7205108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        } catch (IOException e) {
7206108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            Log.e(TAG, "IOException: " + e);
7207108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        } finally {
7208108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            composer.terminate();
7209108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            if (writer != null) {
7210108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                try {
7211108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                    writer.close();
7212108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                } catch (IOException e) {
7213108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                    Log.w(TAG, "IOException during closing output stream: " + e);
7214108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                }
7215d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
7216d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
7217d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
7218b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
72194f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
72204f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
7221415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
7222415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov        waitForAccess(mReadAccessLatch);
7223415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
7224a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
72254f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
7226b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS:
7227be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return Contacts.CONTENT_TYPE;
72282d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill            case CONTACTS_LOOKUP:
7229b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_ID:
7230b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
723124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE:
7232b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_ITEM_TYPE;
7233f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD:
723442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD:
723524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD:
7236f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                return Contacts.CONTENT_VCARD_TYPE;
7237f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            case CONTACTS_ID_PHOTO:
7238bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_PHOTO:
7239bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_ID_PHOTO:
7240f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_ID_DISPLAY_PHOTO:
7241f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_DISPLAY_PHOTO:
7242f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_ID_DISPLAY_PHOTO:
7243f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case RAW_CONTACTS_ID_DISPLAY_PHOTO:
7244f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case DISPLAY_PHOTO:
7245f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                return "image/jpeg";
7246b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS:
724724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS:
7248be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return RawContacts.CONTENT_TYPE;
7249b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS_ID:
725024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID:
7251b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return RawContacts.CONTENT_ITEM_TYPE;
7252f481f22a9323fe338672f99b88b26c5f0725cd42David Brown            case DATA:
725324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA:
7254f481f22a9323fe338672f99b88b26c5f0725cd42David Brown                return Data.CONTENT_TYPE;
7255508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
72565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long id = ContentUris.parseId(uri);
72575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                if (ContactsContract.isProfileId(id)) {
72585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    return mProfileHelper.getDataMimeType(id);
72595d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                } else {
72605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    return mContactsHelper.getDataMimeType(id);
72615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                }
726248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES:
726348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_TYPE;
726448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
726548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_ITEM_TYPE;
72669005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov            case PHONE_LOOKUP:
72679005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov                return PhoneLookup.CONTENT_TYPE;
726848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS:
726948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_TYPE;
727048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
727148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_ITEM_TYPE;
727248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS:
727348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_TYPE;
727448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID:
727548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_ITEM_TYPE;
7276b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
7277b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_TYPE;
7278b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
7279b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_ITEM_TYPE;
7280b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case SETTINGS:
7281b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Settings.CONTENT_TYPE;
7282b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
7283b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_TYPE;
7284c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS:
7285c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SUGGEST_MIME_TYPE;
7286c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT:
7287c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
7288d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES:
7289d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_TYPE;
7290d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID:
7291d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_ITEM_TYPE;
7292af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS:
7293af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                return StreamItems.CONTENT_TYPE;
7294af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS_ID:
7295af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                return StreamItems.CONTENT_ITEM_TYPE;
7296af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS_ID_PHOTOS:
7297af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                return StreamItems.StreamItemPhotos.CONTENT_TYPE;
7298af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS_ID_PHOTOS_ID:
7299af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                return StreamItems.StreamItemPhotos.CONTENT_ITEM_TYPE;
7300af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS_PHOTOS:
7301af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                throw new UnsupportedOperationException("Not supported for write-only URI " + uri);
730261efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov            default:
730361efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov                return mLegacyApiSupport.getType(uri);
73044f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
73054f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
73067e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
730709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    public String[] getDefaultProjection(Uri uri) {
730809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        final int match = sUriMatcher.match(uri);
730909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        switch (match) {
731009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS:
731109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP:
731209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_ID:
731309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
731409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
731524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE:
731609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsProjectionMap.getColumnNames();
731709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
73188727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov            case CONTACTS_ID_ENTITIES:
731924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_ENTITIES:
73208727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov                return sEntityProjectionMap.getColumnNames();
73218727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov
732209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_VCARD:
732309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_MULTI_VCARD:
732424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD:
732509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsVCardProjectionMap.getColumnNames();
732609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
732709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS:
732809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS_ID:
732924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS:
733024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID:
733109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sRawContactsProjectionMap.getColumnNames();
733209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
733309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DATA_ID:
733409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES:
733509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES_ID:
733609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS:
733709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS_ID:
733809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS:
733909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS_ID:
734024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA:
734109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDataProjectionMap.getColumnNames();
734209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
734309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONE_LOOKUP:
734409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sPhoneLookupProjectionMap.getColumnNames();
734509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
734609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
734709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
734809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sAggregationExceptionsProjectionMap.getColumnNames();
734909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
735009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case SETTINGS:
735109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sSettingsProjectionMap.getColumnNames();
735209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
735309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES:
735409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES_ID:
735509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDirectoryProjectionMap.getColumnNames();
735609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
735709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            default:
735809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return null;
735909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
736009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    }
736109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
7362f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private class StructuredNameLookupBuilder extends NameLookupBuilder {
7363f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
7364f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public StructuredNameLookupBuilder(NameSplitter splitter) {
7365f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            super(splitter);
7366f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
7367f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
7368f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
7369f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
7370f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name) {
73715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mDbHelper.get().insertNameLookup(rawContactId, dataId, lookupType, name);
7372f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
7373f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
7374f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
7375f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected String[] getCommonNicknameClusters(String normalizedName) {
7376d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov            return mCommonNicknameCache.getCommonNicknameClusters(normalizedName);
7377f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
7378f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
7379f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
73802d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov    public void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
7381d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
7382d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + RawContacts.CONTACT_ID +
7383d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.RAW_CONTACTS +
7384d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " JOIN " + Tables.NAME_LOOKUP +
7385d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " ON(" + RawContactsColumns.CONCRETE_ID + "="
7386d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
7387d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE normalized_name GLOB '");
7388e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
7389916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE +
7390916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " IN(" + CONTACT_LOOKUP_NAME_TYPES + "))");
7391e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    }
7392e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
73939a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    public boolean isPhoneNumber(String filter) {
73949a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        boolean atLeastOneDigit = false;
73959a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        int len = filter.length();
73969a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        for (int i = 0; i < len; i++) {
73979a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            char c = filter.charAt(i);
73989a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            if (c >= '0' && c <= '9') {
73999a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                atLeastOneDigit = true;
74009a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            } else if (c != '*' && c != '#' && c != '+' && c != 'N' && c != '.' && c != ';'
74019a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                    && c != '-' && c != '(' && c != ')' && c != ' ') {
74029a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                return false;
74039a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            }
74049a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
74059a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return atLeastOneDigit;
74069a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
74079a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
74084a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /**
74097a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * Takes components of a name from the query parameters and returns a cursor with those
74107a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * components as well as all missing components.  There is no database activity involved
74117a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * in this so the call can be made on the UI thread.
74127a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     */
74137a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private Cursor completeName(Uri uri, String[] projection) {
74147a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        if (projection == null) {
74157a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            projection = sDataProjectionMap.getColumnNames();
74167a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
74177a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
74187a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        ContentValues values = new ContentValues();
7419f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        DataRowHandlerForStructuredName handler = (DataRowHandlerForStructuredName)
7420f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                getDataRowHandler(StructuredName.CONTENT_ITEM_TYPE);
74217a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
74227a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        copyQueryParamsToContentValues(values, uri,
74237a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.DISPLAY_NAME,
74247a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PREFIX,
74257a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.GIVEN_NAME,
74267a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.MIDDLE_NAME,
74277a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.FAMILY_NAME,
74287a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.SUFFIX,
74297a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_NAME,
74307a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_FAMILY_NAME,
74317a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_MIDDLE_NAME,
74327a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_GIVEN_NAME
74337a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        );
74347a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
74357a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        handler.fixStructuredNameComponents(values, values);
74367a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
74377a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
74387a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        Object[] row = new Object[projection.length];
74397a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
74407a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            row[i] = values.get(projection[i]);
74417a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
74427a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        cursor.addRow(row);
74437a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        return cursor;
74447a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
74457a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
74467a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private void copyQueryParamsToContentValues(ContentValues values, Uri uri, String... columns) {
74477a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (String column : columns) {
74487a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            String param = uri.getQueryParameter(column);
74497a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            if (param != null) {
74507a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                values.put(column, param);
74517a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
74527a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
74537a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
74547a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
74557a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
74567a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    /**
74574a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     * Inserts an argument at the beginning of the selection arg list.
74584a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     */
74594a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private String[] insertSelectionArg(String[] selectionArgs, String arg) {
7460b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
7461b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
7462b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
7463b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
7464b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
74654a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            newSelectionArgs[0] = arg;
74664a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length);
7467b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
7468b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
7469b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
7470caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
74715e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private String[] appendProjectionArg(String[] projection, String arg) {
74725e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        if (projection == null) {
74735e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            return null;
74745e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        }
74755e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        final int length = projection.length;
74765e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        String[] newProjection = new String[length + 1];
74775e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        System.arraycopy(projection, 0, newProjection, 0, length);
74785e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        newProjection[length] = arg;
74795e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        return newProjection;
74805e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    }
74815e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
7482caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    protected Account getDefaultAccount() {
7483caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        AccountManager accountManager = AccountManager.get(getContext());
7484caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        try {
74855f1f4a062ac34d75d2dbf586702cbeb121cf09caDmitri Plotnikov            Account[] accounts = accountManager.getAccountsByType(DEFAULT_ACCOUNT_TYPE);
7486caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            if (accounts != null && accounts.length > 0) {
7487caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov                return accounts[0];
7488caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            }
7489caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        } catch (Throwable e) {
74906f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov            Log.e(TAG, "Cannot determine the default account for contacts compatibility", e);
7491caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        }
74926f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov        return null;
7493caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    }
7494f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
749573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    /**
749643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * Returns true if the specified account type and data set is writable.
749773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     */
749843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    protected boolean isWritableAccountWithDataSet(String accountTypeAndDataSet) {
749943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        if (accountTypeAndDataSet == null) {
7500bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov            return true;
7501bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        }
7502bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov
750343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        Boolean writable = mAccountWritability.get(accountTypeAndDataSet);
750473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable != null) {
750573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            return writable;
750673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
750773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
7508627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        IContentService contentService = ContentResolver.getContentService();
7509627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
751043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // TODO(dsantoro): Need to update this logic to allow for sub-accounts.
7511627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (SyncAdapterType sync : contentService.getSyncAdapterTypes()) {
7512627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (ContactsContract.AUTHORITY.equals(sync.authority) &&
751343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        accountTypeAndDataSet.equals(sync.accountType)) {
751473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    writable = sync.supportsUploading();
751573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    break;
7516627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
7517627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
7518627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } catch (RemoteException e) {
7519627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            Log.e(TAG, "Could not acquire sync adapter types");
7520627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
752173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
752273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable == null) {
752373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            writable = false;
752473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
752573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
752643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        mAccountWritability.put(accountTypeAndDataSet, writable);
752773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        return writable;
7528627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
7529b4e61e064b59e8076df81b061add9fb358fd2ed9Dmitri Plotnikov
7530d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
7531f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static boolean readBooleanQueryParameter(Uri uri, String parameter,
7532f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean defaultValue) {
7533f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7534f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        // Manually parse the query, which is much faster than calling uri.getQueryParameter
7535f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
7536f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
7537f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
7538f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7539f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7540f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = query.indexOf(parameter);
7541f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (index == -1) {
7542f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
7543f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7544f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7545f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        index += parameter.length();
7546f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7547f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return !matchQueryParameter(query, index, "=0", false)
7548f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && !matchQueryParameter(query, index, "=false", true);
7549f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
7550f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7551f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private static boolean matchQueryParameter(String query, int index, String value,
7552f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean ignoreCase) {
7553f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int length = value.length();
7554f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return query.regionMatches(ignoreCase, index, value, 0, length)
7555f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && (query.length() == index + length || query.charAt(index + length) == '&');
7556f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
7557f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7558f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /**
7559f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * A fast re-implementation of {@link Uri#getQueryParameter}
7560f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     */
7561f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static String getQueryParameter(Uri uri, String parameter) {
7562f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
7563f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
7564f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return null;
7565f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7566f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7567f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int queryLength = query.length();
7568f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int parameterLength = parameter.length();
7569f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7570f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String value;
7571f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = 0;
7572f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        while (true) {
7573f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index = query.indexOf(parameter, index);
7574f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (index == -1) {
7575f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
75765fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            }
75775fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa
75785fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            // Should match against the whole parameter instead of its suffix.
75795fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            // e.g. The parameter "param" must not be found in "some_param=val".
75805fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            if (index > 0) {
75815fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                char prevChar = query.charAt(index - 1);
75825fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                if (prevChar != '?' && prevChar != '&') {
75835fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    // With "some_param=val1&param=val2", we should find second "param" occurrence.
75845fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    index += parameterLength;
75855fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    continue;
75865fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                }
7587f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
7588f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7589f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index += parameterLength;
7590f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7591f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (queryLength == index) {
7592f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
7593f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
7594f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7595f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (query.charAt(index) == '=') {
7596f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                index++;
7597f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                break;
7598f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
7599f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7600f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7601f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int ampIndex = query.indexOf('&', index);
7602f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (ampIndex == -1) {
7603f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index);
7604f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        } else {
7605f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index, ampIndex);
7606f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7607f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7608f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return Uri.decode(value);
7609f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
76105dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
76110dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    protected boolean isAggregationUpgradeNeeded() {
76120dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
76130dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            return false;
76140dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
76150dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
76165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int version = Integer.parseInt(mContactsHelper.getProperty(
76175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                PROPERTY_AGGREGATION_ALGORITHM, "1"));
76180dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        return version < PROPERTY_AGGREGATION_ALGORITHM_VERSION;
76190dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
76200dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
7621bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void upgradeAggregationAlgorithmInBackground() {
76220dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // This upgrade will affect very few contacts, so it can be performed on the
76230dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // main thread during the initial boot after an OTA
76240dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
76250dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        Log.i(TAG, "Upgrading aggregation algorithm");
76260dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int count = 0;
76270dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        long start = SystemClock.currentThreadTimeMillis();
76285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        SQLiteDatabase db = null;
76290dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        try {
76305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToContactMode();
76315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db = mContactsHelper.getWritableDatabase();
76325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.set(db);
76335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db.beginTransaction();
76345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            Cursor cursor = db.query(true,
76350dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    Tables.RAW_CONTACTS + " r1 JOIN " + Tables.RAW_CONTACTS + " r2",
76360dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    new String[]{"r1." + RawContacts._ID},
76370dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    "r1." + RawContacts._ID + "!=r2." + RawContacts._ID +
76380dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.CONTACT_ID + "=r2." + RawContacts.CONTACT_ID +
76390dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.ACCOUNT_NAME + "=r2." + RawContacts.ACCOUNT_NAME +
764043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    " AND r1." + RawContacts.ACCOUNT_TYPE + "=r2." + RawContacts.ACCOUNT_TYPE +
764143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    " AND r1." + RawContacts.DATA_SET + "=r2." + RawContacts.DATA_SET,
76420dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    null, null, null, null, null);
76430dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            try {
76440dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                while (cursor.moveToNext()) {
76450dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    long rawContactId = cursor.getLong(0);
76460dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId,
76470dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                            RawContacts.AGGREGATION_MODE_DEFAULT, true);
76480dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    count++;
76490dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                }
76500dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            } finally {
76510dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                cursor.close();
76520dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            }
76535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mContactAggregator.aggregateInTransaction(mTransactionContext.get(), db);
7654bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            updateSearchIndexInTransaction();
76555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db.setTransactionSuccessful();
76565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mContactsHelper.setProperty(PROPERTY_AGGREGATION_ALGORITHM,
76570dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    String.valueOf(PROPERTY_AGGREGATION_ALGORITHM_VERSION));
76580dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        } finally {
76595d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (db != null) {
76605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                db.endTransaction();
76615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
76620dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            long end = SystemClock.currentThreadTimeMillis();
76630dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            Log.i(TAG, "Aggregation algorithm upgraded for " + count
76640dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    + " contacts, in " + (end - start) + "ms");
76650dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
76660dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
76679a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
76689a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    /* Visible for testing */
76699a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    boolean isPhone() {
76709a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (!sIsPhoneInitialized) {
76719a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sIsPhone = new TelephonyManager(getContext()).isVoiceCapable();
76729a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sIsPhoneInitialized = true;
76739a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
76749a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return sIsPhone;
76759a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
767646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
767746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private boolean handleDataUsageFeedback(Uri uri) {
767846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final long currentTimeMillis = System.currentTimeMillis();
767946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String usageType = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
768046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] ids = uri.getLastPathSegment().trim().split(",");
768146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final ArrayList<Long> dataIds = new ArrayList<Long>();
768246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
768346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        for (String id : ids) {
768446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            dataIds.add(Long.valueOf(id));
768546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
768646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final boolean successful;
768746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (TextUtils.isEmpty(usageType)) {
768846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            Log.w(TAG, "Method for data usage feedback isn't specified. Ignoring.");
768946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            successful = false;
769046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } else {
769146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            successful = updateDataUsageStat(dataIds, usageType, currentTimeMillis) > 0;
769246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
769346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
769446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        // Handle old API. This doesn't affect the result of this entire method.
769546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] questionMarks = new String[ids.length];
769646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        Arrays.fill(questionMarks, "?");
769746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String where = Data._ID + " IN (" + TextUtils.join(",", questionMarks) + ")";
76985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        final Cursor cursor = mActiveDb.get().query(
7699ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                Views.DATA,
770046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                new String[] { Data.CONTACT_ID },
770146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                where, ids, null, null, null);
770246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        try {
770346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            while (cursor.moveToNext()) {
770446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mSelectionArgs1[0] = cursor.getString(0);
770546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                ContentValues values2 = new ContentValues();
770646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                values2.put(Contacts.LAST_TIME_CONTACTED, currentTimeMillis);
77075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mActiveDb.get().update(Tables.CONTACTS, values2, Contacts._ID + "=?",
77085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mSelectionArgs1);
77095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mActiveDb.get().execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
77105d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mActiveDb.get().execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
771146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
771246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } finally {
771346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            cursor.close();
771446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
771546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
771646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        return successful;
771746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
771846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
771946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
772046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Update {@link Tables#DATA_USAGE_STAT}.
772146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     *
772246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * @return the number of rows affected.
772346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
7724f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa    @VisibleForTesting
7725f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa    /* package */ int updateDataUsageStat(
7726f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa            List<Long> dataIds, String type, long currentTimeMillis) {
772746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final int typeInt = sDataUsageTypeMap.get(type);
772846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String where = DataUsageStatColumns.DATA_ID + " =? AND "
772946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                + DataUsageStatColumns.USAGE_TYPE_INT + " =?";
773046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] columns =
773146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                new String[] { DataUsageStatColumns._ID, DataUsageStatColumns.TIMES_USED };
773246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final ContentValues values = new ContentValues();
773346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        for (Long dataId : dataIds) {
773446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            final String[] args = new String[] { dataId.toString(), String.valueOf(typeInt) };
77355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().beginTransaction();
773646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            try {
77375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                final Cursor cursor = mActiveDb.get().query(Tables.DATA_USAGE_STAT, columns, where,
77385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        args, null, null, null);
773946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                try {
774046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (cursor.getCount() > 0) {
774146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        if (!cursor.moveToFirst()) {
774246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            Log.e(TAG,
774346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    "moveToFirst() failed while getAccount() returned non-zero.");
774446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        } else {
774546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.clear();
774646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.put(DataUsageStatColumns.TIMES_USED, cursor.getInt(1) + 1);
774746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.put(DataUsageStatColumns.LAST_TIME_USED, currentTimeMillis);
77485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            mActiveDb.get().update(Tables.DATA_USAGE_STAT, values,
774946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    DataUsageStatColumns._ID + " =?",
775046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    new String[] { cursor.getString(0) });
775146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        }
775246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    } else {
775346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.clear();
775446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.DATA_ID, dataId);
775546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.USAGE_TYPE_INT, typeInt);
775646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.TIMES_USED, 1);
775746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.LAST_TIME_USED, currentTimeMillis);
77585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mActiveDb.get().insert(Tables.DATA_USAGE_STAT, null, values);
775946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    }
77605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mActiveDb.get().setTransactionSuccessful();
776146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                } finally {
776246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    cursor.close();
776346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
776446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            } finally {
77655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mActiveDb.get().endTransaction();
776646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
776746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
776846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
776946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        return dataIds.size();
777046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
777146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
777246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
777346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Returns a sort order String for promoting data rows (email addresses, phone numbers, etc.)
777446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * associated with a primary account. The primary account should be supplied from applications
777546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * with {@link ContactsContract#PRIMARY_ACCOUNT_NAME} and
777646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * {@link ContactsContract#PRIMARY_ACCOUNT_TYPE}. Null will be returned when the primary
777746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * account isn't available.
777846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
777946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private String getAccountPromotionSortOrder(Uri uri) {
778046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String primaryAccountName =
778146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME);
778246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String primaryAccountType =
778346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE);
778446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
778546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        // Data rows associated with primary account should be promoted.
778646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (!TextUtils.isEmpty(primaryAccountName)) {
778746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            StringBuilder sb = new StringBuilder();
778846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            sb.append("(CASE WHEN " + RawContacts.ACCOUNT_NAME + "=");
778946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            DatabaseUtils.appendEscapedSQLString(sb, primaryAccountName);
779046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            if (!TextUtils.isEmpty(primaryAccountType)) {
779146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                sb.append(" AND " + RawContacts.ACCOUNT_TYPE + "=");
779246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DatabaseUtils.appendEscapedSQLString(sb, primaryAccountType);
779346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
779446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            sb.append(" THEN 0 ELSE 1 END)");
779546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            return sb.toString();
779646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } else {
779746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            return null;
779846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
779946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
7800b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
7801b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    /**
7802b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * Checks the URI for a deferred snippeting request
7803b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * @return a boolean indicating if a deferred snippeting request is in the RI
7804b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     */
7805b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private boolean deferredSnippetingRequested(Uri uri) {
7806b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        String deferredSnippeting =
7807b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            getQueryParameter(uri, SearchSnippetColumns.DEFERRED_SNIPPETING_KEY);
7808b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        return !TextUtils.isEmpty(deferredSnippeting) &&  deferredSnippeting.equals("1");
7809b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    }
7810b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
7811b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    /**
7812b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * Checks if query is a single word or not.
7813b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * @return a boolean indicating if the query is one word or not
7814b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     */
7815b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private boolean isSingleWordQuery(String query) {
7816b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        return query.split(QUERY_TOKENIZER_REGEX).length == 1;
7817b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    }
7818b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
7819b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    /**
7820b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * Checks the projection for a SNIPPET column indicating that a snippet is needed
7821b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * @return a boolean indicating if a snippet is needed or not.
7822b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     */
7823b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private boolean snippetNeeded(String [] projection) {
7824b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        return mDbHelper.get().isInProjection(projection, SearchSnippetColumns.SNIPPET);
7825b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    }
78264f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
7827