ContactsProvider2.java revision 3826a44d8de41e9c148dd6a967392ea5af478085
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
1953214b3ed12b0ff9cb589b6559311f2ac142f2e3Bjorn Bringertimport com.android.common.content.SyncStateContentProviderHelper;
205b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikovimport com.android.providers.contacts.ContactAggregator.AggregationSuggestionParameter;
2197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactLookupKey.LookupKeySegment;
2297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.AggregatedPresenceColumns;
2397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns;
2497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.Clauses;
2597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.ContactsColumns;
2697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.ContactsStatusUpdatesColumns;
2797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
2897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
2997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns;
3097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupColumns;
3197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
3297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneColumns;
3397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
3497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
3597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
3697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.SettingsColumns;
3797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.StatusUpdatesColumns;
3897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.Tables;
3997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.vcard.VCardComposer;
4097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.vcard.VCardConfig;
4197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Lists;
4297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Maps;
4397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Sets;
4497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
45b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.accounts.Account;
46caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikovimport android.accounts.AccountManager;
475b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanaimport android.accounts.OnAccountsUpdateListener;
48bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.Notification;
49bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.NotificationManager;
50bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.PendingIntent;
51c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.app.SearchManager;
52568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderOperation;
53568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderResult;
546ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.content.ContentResolver;
5535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.ContentUris;
5667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.ContentValues;
5767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.Context;
58627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.content.IContentService;
59bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.content.Intent;
60568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.OperationApplicationException;
613d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.content.SharedPreferences;
62627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.content.SyncAdapterType;
6367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.UriMatcher;
64f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringertimport android.content.res.AssetFileDescriptor;
651129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikovimport android.database.CharArrayBuffer;
664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.Cursor;
67ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.database.CursorWrapper;
68ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.database.DatabaseUtils;
6909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor;
7009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor.RowBuilder;
71a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikovimport android.database.sqlite.SQLiteConstraintException;
724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteDatabase;
7308ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwoodimport android.database.sqlite.SQLiteDoneException;
744f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteQueryBuilder;
75c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millarimport android.database.sqlite.SQLiteStatement;
764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.net.Uri;
77d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.net.Uri.Builder;
7851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikovimport android.os.AsyncTask;
796ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.os.Bundle;
80ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringertimport android.os.ParcelFileDescriptor;
81b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.os.RemoteException;
820dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikovimport android.os.SystemClock;
830e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport android.os.SystemProperties;
843d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.preference.PreferenceManager;
85508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkeyimport android.provider.BaseColumns;
863de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract;
87b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions;
8897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.BaseTypes;
8997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Email;
9097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
9197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Im;
9297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Nickname;
9397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Organization;
9497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Phone;
9597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Photo;
9697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
9797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
98ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.provider.ContactsContract.ContactCounts;
993de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Contacts;
1005b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikovimport android.provider.ContactsContract.Contacts.AggregationSuggestions;
1013de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Data;
102d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.provider.ContactsContract.Directory;
1035dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikovimport android.provider.ContactsContract.DisplayNameSources;
1045dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikovimport android.provider.ContactsContract.FullNameStyle;
1053de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Groups;
106bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.provider.ContactsContract.Intents;
1073de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.PhoneLookup;
1085dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikovimport android.provider.ContactsContract.PhoneticNameStyle;
10909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.provider.ContactsContract.ProviderStatus;
1103de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.RawContacts;
111916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikovimport android.provider.ContactsContract.SearchSnippetColumns;
1123de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Settings;
11382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikovimport android.provider.ContactsContract.StatusUpdates;
11497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.LiveFolders;
11597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.OpenableColumns;
11697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.SyncStateContract;
117a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.telephony.PhoneNumberUtils;
118a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.text.TextUtils;
119c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.util.Log;
1204f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
121d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.ByteArrayOutputStream;
122b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport java.io.FileNotFoundException;
123d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.IOException;
124d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.OutputStream;
12542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.text.SimpleDateFormat;
1267e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport java.util.ArrayList;
1275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.Collections;
12842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.util.Date;
129b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport java.util.HashMap;
1300e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.HashSet;
1315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.List;
132622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkeyimport java.util.Locale;
133b5a4add17815167d20a90645779df34cdf45280dFred Quintanaimport java.util.Map;
1340e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.Set;
135ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikovimport java.util.concurrent.CountDownLatch;
1364f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1374f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/**
1384f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Contacts content provider. The contract between this provider and applications
1394f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * is defined in {@link ContactsContract}.
1404f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
1415b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanapublic class ContactsProvider2 extends SQLiteContentProvider implements OnAccountsUpdateListener {
142caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
143bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final String TAG = "ContactsProvider";
144bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov
145bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE);
1464f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
147619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: carefully prevent all incoming nested queries; they can be gaping security holes
148619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: check for restricted flag during insert(), update(), and delete() calls
149619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Default for the maximum number of returned aggregation suggestions. */
1513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DEFAULT_MAX_SUGGESTIONS = 5;
1523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1533d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /**
154b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov     * Property key for the legacy contact import version. The need for a version
1553d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * as opposed to a boolean flag is that if we discover bugs in the contact import process,
1563d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * we can trigger re-import by incrementing the import version.
1573d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     */
158b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final String PROPERTY_CONTACTS_IMPORTED = "contacts_imported_v1";
159b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final int PROPERTY_CONTACTS_IMPORT_VERSION = 1;
16051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    private static final String PREF_LOCALE = "locale";
1613d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1620dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static final String PROPERTY_AGGREGATION_ALGORITHM = "aggregation_v2";
1630dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static final int PROPERTY_AGGREGATION_ALGORITHM_VERSION = 2;
1640dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
1650e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff    private static final String AGGREGATE_CONTACTS = "sync.contacts.aggregate";
1660e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff
167a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
1684f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
169dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov    private static final String TIMES_CONTACTED_SORT_COLUMN = "times_contacted_sort";
1705e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
171d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final String STREQUENT_ORDER_BY = Contacts.STARRED + " DESC, "
172dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov            + TIMES_CONTACTED_SORT_COLUMN + " DESC, "
1739b43551f1ce33b79141772737a262ce609bd0cebMegha Joshi            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
174d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final String STREQUENT_LIMIT =
175d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            "(SELECT COUNT(1) FROM " + Tables.CONTACTS + " WHERE "
176d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            + Contacts.STARRED + "=1) + 25";
177d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
1786e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_CONTACTS_TABLE =
1799b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.CONTACTS + " SET " + Contacts.TIMES_CONTACTED + "=" +
1809b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + Contacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
1819b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + Contacts.TIMES_CONTACTED + " + 1) END WHERE " + Contacts._ID + "=?";
1829b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
1836e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE =
1849b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.RAW_CONTACTS + " SET " + RawContacts.TIMES_CONTACTED + "=" +
1859b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + RawContacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
1869b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + RawContacts.TIMES_CONTACTED + " + 1) END WHERE " + RawContacts.CONTACT_ID + "=?";
1879b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
188de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa    /* package */ static final String PHONEBOOK_COLLATOR_NAME = "PHONEBOOK";
189de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa
190d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS = 1000;
191d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS_ID = 1001;
1925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP = 1002;
1935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID = 1003;
194a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_DATA = 1004;
1955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_FILTER = 1005;
1965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT = 1006;
1975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT_FILTER = 1007;
1985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_GROUP = 1008;
199a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_PHOTO = 1009;
200f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    private static final int CONTACTS_AS_VCARD = 1010;
20142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann    private static final int CONTACTS_AS_MULTI_VCARD = 1011;
2022149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_DATA = 1012;
2032149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID_DATA = 1013;
204a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_ENTITIES = 1014;
205a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_ENTITIES = 1015;
206a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID_ENTITIES = 1016;
2074f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2085ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS = 2002;
2095ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_ID = 2003;
2105ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_DATA = 2004;
21146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITY_ID = 2005;
2124f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2136bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
2146bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
215ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
21648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_ID = 3003;
21748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_FILTER = 3004;
21848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS = 3005;
21948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_ID = 3006;
22048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_LOOKUP = 3007;
22148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_FILTER = 3008;
22248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS = 3009;
22348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS_ID = 3010;
224a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2256bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
2266bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
227b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
228b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
229b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
23082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES = 7000;
23182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES_ID = 7001;
2321f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
23331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
23431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
235eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final int SETTINGS = 9000;
236eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
237ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
238ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
239ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
240ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
24135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
242b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private static final int SYNCSTATE_ID = 11001;
24335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
244c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SUGGESTIONS = 12001;
245c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SHORTCUT = 12002;
246c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
2471b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS = 14000;
2481b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_WITH_PHONES = 14001;
2491b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_FAVORITES = 14002;
2501b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_GROUP_NAME = 14003;
2511b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
25246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITIES = 15001;
25346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
25409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private static final int PROVIDER_STATUS = 16001;
25509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
256d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES = 17001;
257d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES_ID = 17002;
258d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
2597a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private static final int COMPLETE_NAME = 18000;
2607a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
261dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID =
262dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
263dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME
264dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
265dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE
266dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_TYPE
267dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " AND " + Groups.FAVORITES + " != 0";
268dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
269dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID =
270dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
271dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
272dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
273dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
274dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AND "
275dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + Groups.AUTO_ADD + " != 0";
276dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
277dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String[] PROJECTION_GROUP_ID
278dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            = new String[]{Tables.GROUPS + "." + Groups._ID};
279dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
280dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_GROUPMEMBERSHIP_DATA = DataColumns.MIMETYPE_ID + "=? "
281dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.GROUP_ROW_ID + "=? "
282dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.RAW_CONTACT_ID + "=?";
283dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
284dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_STARRED_FROM_RAW_CONTACTS =
285dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            "SELECT " + RawContacts.STARRED
286dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " FROM " + Tables.RAW_CONTACTS + " WHERE " + RawContacts._ID + "=?";
287dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
288d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private interface DataContactsQuery {
289f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        public static final String TABLE = "data "
290f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) "
291f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN contacts ON (raw_contacts.contact_id = contacts._id)";
29267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
29367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
2946cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
2953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
296f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            ContactsColumns.CONCRETE_ID
297ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
298ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
299d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
30067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA_ID = 1;
301d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int CONTACT_ID = 2;
302ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
3031f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
30414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    private interface DataDeleteQuery {
30567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String TABLE = Tables.DATA_JOIN_MIMETYPES;
3063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
30788e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        public static final String[] CONCRETE_COLUMNS = new String[] {
3083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
3093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            MimetypesColumns.MIMETYPE,
3105ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            Data.RAW_CONTACT_ID,
3113cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.IS_PRIMARY,
312f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            Data.DATA1,
31388e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        };
31488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov
31588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
31688e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data._ID,
31788e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            MimetypesColumns.MIMETYPE,
31888e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data.RAW_CONTACT_ID,
31988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data.IS_PRIMARY,
320f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            Data.DATA1,
3213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        };
3223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
32314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public static final int _ID = 0;
3243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int MIMETYPE = 1;
3255ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 2;
3263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int IS_PRIMARY = 3;
327f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public static final int DATA1 = 4;
3283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
3293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
33014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    private interface DataUpdateQuery {
331321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        String[] COLUMNS = { Data._ID, Data.RAW_CONTACT_ID, Data.MIMETYPE };
33220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
33320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int _ID = 0;
334321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        int RAW_CONTACT_ID = 1;
335321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        int MIMETYPE = 2;
33620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
33720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
338f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
33919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    private interface RawContactsQuery {
34019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String TABLE = Tables.RAW_CONTACTS;
34119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
34219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String[] COLUMNS = new String[] {
343ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.DELETED,
344ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
345ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
34619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        };
34719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
34819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int DELETED = 0;
349ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_TYPE = 1;
350ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_NAME = 2;
35119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    }
35219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
353c76cdd0723b99f478c9ba5329d14a971cd8dfb3dCostin Manolache    public static final String DEFAULT_ACCOUNT_TYPE = "com.google";
354df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana    public static final String FEATURE_LEGACY_HOSTED_OR_GOOGLE = "legacy_hosted_or_google";
355caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
35671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    /** Sql where statement for filtering on groups. */
35771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private static final String CONTACTS_IN_GROUP_SELECT =
35871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov            Contacts._ID + " IN "
35971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + "(SELECT " + RawContacts.CONTACT_ID
36071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " FROM " + Tables.RAW_CONTACTS
36171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + " IN "
36271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
36371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " FROM " + Tables.DATA_JOIN_MIMETYPES
36471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " WHERE " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
36571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "' AND " + GroupMembership.GROUP_ROW_ID + "="
36671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "(SELECT " + Tables.GROUPS + "." + Groups._ID
36771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " FROM " + Tables.GROUPS
36871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " WHERE " + Groups.TITLE + "=?)))";
36971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov
370a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating DIRTY flag on multiple raw contacts */
371a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_DIRTY_SQL =
372a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
373a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.DIRTY + "=1" +
374a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
375a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
376a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating VERSION on multiple raw contacts */
377a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_VERSION_SQL =
378a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
379a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.VERSION + " = " + RawContacts.VERSION + " + 1" +
380a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
381a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
382c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Current contacts - those contacted within the last 3 days (in seconds)
383c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_CURRENT = 3 * 24 * 60 * 60;
384c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
385c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Recent contacts - those contacted within the last 30 days (in seconds)
386c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_RECENT = 30 * 24 * 60 * 60;
387c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
388c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final String TIME_SINCE_LAST_CONTACTED =
389c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            "(strftime('%s', 'now') - " + Contacts.LAST_TIME_CONTACTED + "/1000)";
390c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
391c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    /*
392c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * Sorting order for email address suggestions: first starred, then the rest.
393c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * Within the starred/unstarred groups - three buckets: very recently contacted, then fairly
394c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * recently contacted, then the rest.  Within each of the bucket - descending count
395c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * of times contacted. If all else fails, alphabetical.  (Super)primary email
396c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * address is returned before other addresses for the same contact.
397c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     */
398c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final String EMAIL_FILTER_SORT_ORDER =
399c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            "(CASE WHEN " + Contacts.STARRED + "=1 THEN 0 ELSE 1 END), "
400c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            + "(CASE WHEN " + TIME_SINCE_LAST_CONTACTED + " < " + EMAIL_FILTER_CURRENT + " THEN 0 "
401c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            + " WHEN " + TIME_SINCE_LAST_CONTACTED + " < " + EMAIL_FILTER_RECENT + " THEN 1 "
402c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            + " ELSE 2 END),"
403c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            + Contacts.TIMES_CONTACTED + " DESC, "
404c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            + Contacts.DISPLAY_NAME + ", "
405c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            + Data.CONTACT_ID + ", "
406c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            + Data.IS_SUPER_PRIMARY + " DESC";
407c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
408916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Name lookup types used for contact filtering */
409916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private static final String CONTACT_LOOKUP_NAME_TYPES =
410916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NAME_COLLATION_KEY + "," +
411916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.EMAIL_BASED_NICKNAME + "," +
412916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NICKNAME + "," +
413916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NAME_SHORTHAND + "," +
414f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee            NameLookupType.ORGANIZATION + "," +
415f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee            NameLookupType.NAME_CONSONANTS;
416916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
417f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    /**
418f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * If any of these columns are used in a Data projection, there is no point in
419f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * using the DISTINCT keyword, which can negatively affect performance.
420f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     */
421f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    private static final String[] DISTINCT_DATA_PROHIBITING_COLUMNS = {
422f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data._ID,
423f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.RAW_CONTACT_ID,
424f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.NAME_RAW_CONTACT_ID,
425f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_NAME,
426f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_TYPE,
427f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.DIRTY,
428f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.NAME_VERIFIED,
429f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.SOURCE_ID,
430f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.VERSION,
431f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    };
432916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
433f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsColumns = ProjectionMap.builder()
434f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CUSTOM_RINGTONE)
435f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME)
436f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_ALTERNATIVE)
437f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_SOURCE)
438f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.IN_VISIBLE_GROUP)
439f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LAST_TIME_CONTACTED)
440f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LOOKUP_KEY)
441f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME)
442f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME_STYLE)
443f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHOTO_ID)
4443d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_URI)
4453d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_THUMBNAIL_URI)
446f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SEND_TO_VOICEMAIL)
447f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_ALTERNATIVE)
448f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_PRIMARY)
449f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.STARRED)
450f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.TIMES_CONTACTED)
451cf832869bcf91b8037d8b7f510a3a213b30764a3Dmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
452f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
453f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
454f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsPresenceColumns = ProjectionMap.builder()
455f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
456f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE)
457f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
458f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
459f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
460f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
461f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
462f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
463f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
464f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
465f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
466f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
467f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
468f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
469f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
470f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
471f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSnippetColumns = ProjectionMap.builder()
472f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_MIMETYPE)
473f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_DATA_ID)
474f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_DATA1)
475f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_DATA2)
476f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_DATA3)
477f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_DATA4)
478f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
479f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
480f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
481f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactColumns = ProjectionMap.builder()
482f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_NAME)
483f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_TYPE)
484f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DIRTY)
485f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.NAME_VERIFIED)
486f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SOURCE_ID)
487f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.VERSION)
488f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
489f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
490f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactSyncColumns = ProjectionMap.builder()
491f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC1)
492f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC2)
493f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC3)
494f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC4)
495f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
496f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
497f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataColumns = ProjectionMap.builder()
498f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA1)
499f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA2)
500f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA3)
501f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA4)
502f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA5)
503f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA6)
504f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA7)
505f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA8)
506f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA9)
507f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA10)
508f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA11)
509f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA12)
510f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA13)
511f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA14)
512f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA15)
513f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA_VERSION)
514f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_PRIMARY)
515f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_SUPER_PRIMARY)
516f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.MIMETYPE)
517f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RES_PACKAGE)
518f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC1)
519f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC2)
520f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC3)
521f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC4)
522f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(GroupMembership.GROUP_SOURCE_ID)
523f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
524f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
525f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactPresenceColumns = ProjectionMap.builder()
526f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
527f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.PRESENCE)
528f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
529f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.CHAT_CAPABILITY)
530f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
531f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
532f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
533f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
534f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
535f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
536f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
537f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
538f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
539f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
540f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
541f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
542f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataPresenceColumns = ProjectionMap.builder()
543f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.PRESENCE, Tables.PRESENCE + "." + StatusUpdates.PRESENCE)
544f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CHAT_CAPABILITY, Tables.PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
545f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS, StatusUpdatesColumns.CONCRETE_STATUS)
546f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_TIMESTAMP, StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
547f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_RES_PACKAGE, StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
548f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_LABEL, StatusUpdatesColumns.CONCRETE_STATUS_LABEL)
549f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_ICON, StatusUpdatesColumns.CONCRETE_STATUS_ICON)
550f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
551f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
552038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    /** Contains just BaseColumns._COUNT */
553f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sCountProjectionMap = ProjectionMap.builder()
554f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(BaseColumns._COUNT, "COUNT(*)")
555f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
556f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
557e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    /** Contains just the contacts columns */
558f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionMap = ProjectionMap.builder()
559f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts._ID)
560f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
561f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.NAME_RAW_CONTACT_ID)
562f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
563f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsPresenceColumns)
564f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
565f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
566916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Contains just the contacts columns */
567f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionWithSnippetMap = ProjectionMap.builder()
568f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
569f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sSnippetColumns)
570f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
571916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5725e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    /** Used for pushing starred contacts to the top of a times contacted list **/
573f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentStarredProjectionMap = ProjectionMap.builder()
574f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
575f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(TIMES_CONTACTED_SORT_COLUMN, String.valueOf(Long.MAX_VALUE))
576f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
577f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
578f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentFrequentProjectionMap = ProjectionMap.builder()
579f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
580f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(TIMES_CONTACTED_SORT_COLUMN, Contacts.TIMES_CONTACTED)
581f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
582f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
583f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    /** Contains just the contacts vCard columns */
584f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsVCardProjectionMap = ProjectionMap.builder()
585f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.DISPLAY_NAME, Contacts.DISPLAY_NAME + " || '.vcf'")
586f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.SIZE, "NULL")
587f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
588f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
589ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    /** Contains just the raw contacts columns */
590f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactsProjectionMap = ProjectionMap.builder()
591f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
592f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
593f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
594f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_PRIMARY)
595f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_ALTERNATIVE)
596f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_SOURCE)
597f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME)
598f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME_STYLE)
599f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_PRIMARY)
600f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_ALTERNATIVE)
601f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.TIMES_CONTACTED)
602f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.LAST_TIME_CONTACTED)
603f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CUSTOM_RINGTONE)
604f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SEND_TO_VOICEMAIL)
605f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
606f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.AGGREGATION_MODE)
607f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
608f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
609f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
610f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
611a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the raw entity view*/
612f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawEntityProjectionMap = ProjectionMap.builder()
613f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
614f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
615f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.Entity.DATA_ID)
616f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.IS_RESTRICTED)
617f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
618f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
619f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
620f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
621f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
622f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
623f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
624a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the contact entity view*/
625f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sEntityProjectionMap = ProjectionMap.builder()
626f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity._ID)
627f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.CONTACT_ID)
628f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.RAW_CONTACT_ID)
629f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DATA_ID)
630f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.NAME_RAW_CONTACT_ID)
631f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DELETED)
632f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.IS_RESTRICTED)
633f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
634f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
635f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
636f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
637f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
638f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
639f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
640f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6414a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /** Contains columns from the data view */
642f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataProjectionMap = ProjectionMap.builder()
643f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID)
644f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RAW_CONTACT_ID)
645f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CONTACT_ID)
646f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.NAME_RAW_CONTACT_ID)
647f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
648f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
649f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
650f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
651f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
652f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
653f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6545e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /** Contains columns from the data view */
655f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDistinctDataProjectionMap = ProjectionMap.builder()
656f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID, "MIN(" + Data._ID + ")")
657f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
658f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
659f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
660f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
661f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
662f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
663f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6649261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
665f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sPhoneLookupProjectionMap = ProjectionMap.builder()
666f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup._ID, "contacts_view." + Contacts._ID)
667f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LOOKUP_KEY, "contacts_view." + Contacts.LOOKUP_KEY)
668f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.DISPLAY_NAME, "contacts_view." + Contacts.DISPLAY_NAME)
669f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LAST_TIME_CONTACTED, "contacts_view." + Contacts.LAST_TIME_CONTACTED)
670f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TIMES_CONTACTED, "contacts_view." + Contacts.TIMES_CONTACTED)
671f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.STARRED, "contacts_view." + Contacts.STARRED)
672f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.IN_VISIBLE_GROUP, "contacts_view." + Contacts.IN_VISIBLE_GROUP)
673f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.PHOTO_ID, "contacts_view." + Contacts.PHOTO_ID)
6743d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_URI, "contacts_view." + Contacts.PHOTO_URI)
6753d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_THUMBNAIL_URI, "contacts_view." + Contacts.PHOTO_THUMBNAIL_URI)
676f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.CUSTOM_RINGTONE, "contacts_view." + Contacts.CUSTOM_RINGTONE)
677f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.HAS_PHONE_NUMBER, "contacts_view." + Contacts.HAS_PHONE_NUMBER)
678f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.SEND_TO_VOICEMAIL, "contacts_view." + Contacts.SEND_TO_VOICEMAIL)
679f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.NUMBER, Phone.NUMBER)
680f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TYPE, Phone.TYPE)
681f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LABEL, Phone.LABEL)
6822530512f639c4979fd7371c7dd25dd67e8118124Bai Tao            .add(PhoneLookup.NORMALIZED_NUMBER, Phone.NORMALIZED_NUMBER)
683f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
684f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
685ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
686f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsProjectionMap = ProjectionMap.builder()
687f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups._ID)
688f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_NAME)
689f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_TYPE)
690f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SOURCE_ID)
691f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DIRTY)
692f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.VERSION)
693f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.RES_PACKAGE)
694f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE)
695f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE_RES)
696f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.GROUP_VISIBLE)
697f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYSTEM_ID)
698f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DELETED)
699f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.NOTES)
700f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SHOULD_SYNC)
701f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.FAVORITES)
702f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.AUTO_ADD)
703f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC1)
704f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC2)
705f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC3)
706f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC4)
707f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
708f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
709ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains {@link Groups} columns along with summary details */
710f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsSummaryProjectionMap = ProjectionMap.builder()
711f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sGroupsProjectionMap)
712f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_COUNT,
713f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
714f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS
715f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " WHERE " + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP
716f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Clauses.BELONGS_TO_GROUP
717f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ")")
718f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_WITH_PHONES,
719f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
720f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS
721f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " WHERE " + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP
722f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Clauses.BELONGS_TO_GROUP
723f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Contacts.HAS_PHONE_NUMBER + ")")
724f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
725f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
726373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains the agg_exceptions columns */
727f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sAggregationExceptionsProjectionMap = ProjectionMap.builder()
728f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id")
729f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.TYPE)
730f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID1)
731f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID2)
732f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
733f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
734eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    /** Contains the agg_exceptions columns */
735f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSettingsProjectionMap = ProjectionMap.builder()
736f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_NAME)
737f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_TYPE)
738f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_VISIBLE)
739f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.SHOULD_SYNC)
740f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ANY_UNSYNCED,
741f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN MIN(" + Settings.SHOULD_SYNC
742f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                        + ",(SELECT "
743f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + "(CASE WHEN MIN(" + Groups.SHOULD_SYNC + ") IS NULL"
744f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " THEN 1"
745f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " ELSE MIN(" + Groups.SHOULD_SYNC + ")"
746f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " END)"
747f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.GROUPS
748f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
749f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_NAME
750f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
751f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_TYPE + "))=0"
752f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN 1"
753f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE 0"
754f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " END)")
755f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_COUNT,
756f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
757f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
758f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
759f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
760f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
761f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
762f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_WITH_PHONES,
763f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
764f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
765f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
766f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + Contacts.HAS_PHONE_NUMBER
767f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
768f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
769f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
770f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
771f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
77282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Contains StatusUpdates columns */
773f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStatusUpdatesProjectionMap = ProjectionMap.builder()
774f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PresenceColumns.RAW_CONTACT_ID)
775f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.DATA_ID, DataColumns.CONCRETE_ID)
776f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_ACCOUNT)
777f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_HANDLE)
778f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PROTOCOL)
779f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // We cannot allow a null in the custom protocol field, because SQLite3 does not
780f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // properly enforce uniqueness of null values
781f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CUSTOM_PROTOCOL,
782f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN " + StatusUpdates.CUSTOM_PROTOCOL + "=''"
783f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN NULL"
784f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE " + StatusUpdates.CUSTOM_PROTOCOL + " END)")
785f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PRESENCE)
786f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CHAT_CAPABILITY)
787f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS)
788f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_TIMESTAMP)
789f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_RES_PACKAGE)
790f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_ICON)
791f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_LABEL)
792f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
793f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
7941b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    /** Contains Live Folders columns */
795f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sLiveFoldersProjectionMap = ProjectionMap.builder()
796f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders._ID, Contacts._ID)
797f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders.NAME, Contacts.DISPLAY_NAME)
798f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // TODO: Put contact photo back when we have a way to display a default icon
799f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // for contacts without a photo
800f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // .add(LiveFolders.ICON_BITMAP, Photos.DATA)
801f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
802f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
803d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /** Contains {@link Directory} columns */
804f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDirectoryProjectionMap = ProjectionMap.builder()
805f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory._ID)
806f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.PACKAGE_NAME)
807f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.TYPE_RESOURCE_ID)
808f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DISPLAY_NAME)
809f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DIRECTORY_AUTHORITY)
810f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_TYPE)
811f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_NAME)
812f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.EXPORT_SUPPORT)
813778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.SHORTCUT_SUPPORT)
814778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.PHOTO_SUPPORT)
815f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
8167e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
8179705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    // where clause to update the status_updates table
8189705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private static final String WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE =
8199705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdatesColumns.DATA_ID + " IN (SELECT Distinct " + StatusUpdates.DATA_ID +
8209705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " FROM " + Tables.STATUS_UPDATES + " LEFT OUTER JOIN " + Tables.PRESENCE +
8219705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " ON " + StatusUpdatesColumns.DATA_ID + " = " + StatusUpdates.DATA_ID + " WHERE ";
8229705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
8232526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private static final String[] EMPTY_STRING_ARRAY = new String[0];
8242526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
825bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
826bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Notification ID for failure to import contacts.
827bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
828bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private static final int LEGACY_IMPORT_FAILED_NOTIFICATION = 1;
82951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
830c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Precompiled sql statement for setting a data record to the primary. */
831c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetPrimaryStatement;
8323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for setting a data record to the super primary. */
833c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetSuperPrimaryStatement;
8343cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for updating a contact display name */
83525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov    private SQLiteStatement mRawContactDisplayNameUpdate;
836f4015ab9ab7c26b766b5331fbf6655b8c54877eaDmitri Plotnikov
837f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private SQLiteStatement mNameLookupInsert;
838f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private SQLiteStatement mNameLookupDelete;
839a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateAutoTimestamp;
840a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateInsert;
841a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateReplace;
8420a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private SQLiteStatement mStatusAttributionUpdate;
843a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateDelete;
844f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov    private SQLiteStatement mResetNameVerifiedForOtherRawContacts;
845a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
846f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private long mMimeTypeIdEmail;
847f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private long mMimeTypeIdIm;
8481129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private long mMimeTypeIdStructuredName;
8491129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private long mMimeTypeIdOrganization;
8501129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private long mMimeTypeIdNickname;
8511129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private long mMimeTypeIdPhone;
852f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private StringBuilder mSb = new StringBuilder();
8531129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs1 = new String[1];
8541129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs2 = new String[2];
8552526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private ArrayList<String> mSelectionArgs = Lists.newArrayList();
8562526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
857f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private Account mAccount;
858f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
8594f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
8604f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
861a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
862d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
863d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
864a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_ID_DATA);
865a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/entities", CONTACTS_ID_ENTITIES);
8663653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
8673653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
8682d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*",
8692d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                AGGREGATION_SUGGESTIONS);
870a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_ID_PHOTO);
871c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter", CONTACTS_FILTER);
8725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER);
8735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP);
8742149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/data", CONTACTS_LOOKUP_DATA);
8755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
8762149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/data",
8772149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                CONTACTS_LOOKUP_ID_DATA);
878a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/entities",
879a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ENTITIES);
880a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/entities",
881a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ID_ENTITIES);
882f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_vcard/*", CONTACTS_AS_VCARD);
88342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_multi_vcard/*",
88442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                CONTACTS_AS_MULTI_VCARD);
8855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT);
886ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*",
887ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                CONTACTS_STREQUENT_FILTER);
8885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP);
8893653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
8905ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
8915ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
8925ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA);
89346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ENTITY_ID);
89446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
89546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contact_entities", RAW_CONTACT_ENTITIES);
896b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
8974f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
8984f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
899ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
90048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID);
9015e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER);
902ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
9034a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS);
90448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID);
9051dac83b8fa58944acfd00f44e717a7dddc659d2dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup", EMAILS_LOOKUP);
9065e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP);
9075e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER);
9084a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
909ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
91048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
9111f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
912ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
913ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
914ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
915ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
91635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
917b5a4add17815167d20a90645779df34cdf45280dFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#",
918b5a4add17815167d20a90645779df34cdf45280dFred Quintana                SYNCSTATE_ID);
91935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
920a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
921b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
922b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
923b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
924b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
9254f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
926eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS);
927eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
92882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates", STATUS_UPDATES);
92982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates/#", STATUS_UPDATES_ID);
9301f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
931c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
932c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
933c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
934c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
9352d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*",
936c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SHORTCUT);
937c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
9381b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts",
9391b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS);
9401b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts/*",
9411b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_GROUP_NAME);
9421b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts_with_phones",
9431b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_WITH_PHONES);
9441b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/favorites",
9451b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_FAVORITES);
94609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
94709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "provider_status", PROVIDER_STATUS);
948d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
949d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories", DIRECTORIES);
950d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories/#", DIRECTORIES_ID);
9517a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
9527a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "complete_name", COMPLETE_NAME);
95319a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    }
95419a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
955d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static class DirectoryInfo {
956d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String authority;
957d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountName;
958d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountType;
959d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
960d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
961d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
962d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Cached information about contact directories.
963d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
9644458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private HashMap<String, DirectoryInfo> mDirectoryCache = new HashMap<String, DirectoryInfo>();
9654458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private boolean mDirectoryCacheValid = false;
966d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
9673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
9683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     * Handles inserts and update for a specific Data type.
9693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     */
9703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private abstract class DataRowHandler {
9713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected final String mMimetype;
973653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        protected long mMimetypeId;
9743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9751129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        @SuppressWarnings("all")
9763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public DataRowHandler(String mimetype) {
9773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mMimetype = mimetype;
978a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
979a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            // To ensure the data column position. This is dead code if properly configured.
980a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            if (StructuredName.DISPLAY_NAME != Data.DATA1 || Nickname.NAME != Data.DATA1
981a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    || Organization.COMPANY != Data.DATA1 || Phone.NUMBER != Data.DATA1
982a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    || Email.DATA != Data.DATA1) {
983a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                throw new AssertionError("Some of ContactsContract.CommonDataKinds class primary"
984a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                        + " data is not in DATA1 column");
985a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            }
9863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
988653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        protected long getMimeTypeId() {
989653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (mMimetypeId == 0) {
990b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                mMimetypeId = mDbHelper.getMimeTypeId(mMimetype);
991653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
992653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return mMimetypeId;
993653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
994653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
9953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
9963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Inserts a row into the {@link Data} table.
9973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
9985ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
999e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            final long dataId = db.insert(Tables.DATA, null, values);
1000e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1001e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            Integer primary = values.getAsInteger(Data.IS_PRIMARY);
1002e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (primary != null && primary != 0) {
1003653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, getMimeTypeId());
1004e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
1005e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1006e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return dataId;
10073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
10103cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Validates data and updates a {@link Data} row using the cursor, which contains
10113cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * the current data.
1012813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov         *
1013813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov         * @return true if update changed something
10143cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
1015813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1016f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
101714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
101814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1019653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1020653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (values.containsKey(Data.IS_SUPER_PRIMARY)) {
1021653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                long mimeTypeId = getMimeTypeId();
1022653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsSuperPrimary(rawContactId, dataId, mimeTypeId);
1023653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, mimeTypeId);
1024653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1025653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                // Now that we've taken care of setting these, remove them from "values".
1026653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_SUPER_PRIMARY);
1027653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_PRIMARY);
1028653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            } else if (values.containsKey(Data.IS_PRIMARY)) {
1029653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, getMimeTypeId());
1030653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1031653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                // Now that we've taken care of setting this, remove it from "values".
1032653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_PRIMARY);
1033653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1034653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1035653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (values.size() > 0) {
10364da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
10374da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mDb.update(Tables.DATA, values, Data._ID + " =?", mSelectionArgs1);
1038653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1039653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1040f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (!callerIsSyncAdapter) {
1041653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setRawContactDirty(rawContactId);
1042653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1043813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1044813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
10453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
104814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
104914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
105014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            boolean primary = c.getInt(DataDeleteQuery.IS_PRIMARY) != 0;
10514da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(dataId);
10524da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            int count = db.delete(Tables.DATA, Data._ID + "=?", mSelectionArgs1);
10534da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(rawContactId);
10544da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            db.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=?", mSelectionArgs1);
10553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (count != 0 && primary) {
10565ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                fixPrimary(db, rawContactId);
10573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
10583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            return count;
10593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10615ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        private void fixPrimary(SQLiteDatabase db, long rawContactId) {
10624da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            long mimeTypeId = getMimeTypeId();
1063e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            long primaryId = -1;
1064e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            int primaryType = -1;
10654da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(rawContactId);
10664da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            Cursor c = db.query(DataDeleteQuery.TABLE,
10674da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    DataDeleteQuery.CONCRETE_COLUMNS,
10684da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    Data.RAW_CONTACT_ID + "=?" +
10694da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        " AND " + DataColumns.MIMETYPE_ID + "=" + mimeTypeId,
10704da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1, null, null, null);
10713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            try {
1072e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                while (c.moveToNext()) {
107314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                    long dataId = c.getLong(DataDeleteQuery._ID);
1074f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    int type = c.getInt(DataDeleteQuery.DATA1);
1075e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    if (primaryType == -1 || getTypeRank(type) < getTypeRank(primaryType)) {
1076e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryId = dataId;
1077e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryType = type;
1078e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    }
10793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
10803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            } finally {
10813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                c.close();
10823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
10834da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            if (primaryId != -1) {
10844da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                setIsPrimary(rawContactId, primaryId, mimeTypeId);
10854da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            }
1086e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1087e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1088e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        /**
1089e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * Returns the rank of a specific record type to be used in determining the primary
1090e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * row. Lower number represents higher priority.
1091e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         */
1092e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
1093e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return 0;
10943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
109625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        protected void fixRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
1097285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (!isNewRawContact(rawContactId)) {
1098d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                updateRawContactDisplayName(db, rawContactId);
1099fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                mContactAggregator.updateDisplayNameForRawContact(db, rawContactId);
1100285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
11013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
1102a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1103622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1104622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Return set of values, using current values at given {@link Data#_ID}
1105813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov         * as baseline, but augmented with any updates.  Returns null if there is
1106813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov         * no change.
1107622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1108622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public ContentValues getAugmentedValues(SQLiteDatabase db, long dataId,
1109622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                ContentValues update) {
1110813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            boolean changing = false;
1111622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues values = new ContentValues();
11124da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(dataId);
11134da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            final Cursor cursor = db.query(Tables.DATA, null, Data._ID + "=?",
11144da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1, null, null, null);
1115622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            try {
1116622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                if (cursor.moveToFirst()) {
1117622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    for (int i = 0; i < cursor.getColumnCount(); i++) {
1118622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        final String key = cursor.getColumnName(i);
1119813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                        final String value = cursor.getString(i);
1120813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                        if (!changing && update.containsKey(key)) {
1121813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                            Object newValue = update.get(key);
1122813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                            String newString = newValue == null ? null : newValue.toString();
1123813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                            changing |= !TextUtils.equals(newString, value);
1124813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                        }
1125813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                        values.put(key, value);
1126622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    }
1127622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                }
1128622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            } finally {
1129622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                cursor.close();
1130622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
1131813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!changing) {
1132813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return null;
1133813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1134813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1135622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            values.putAll(update);
1136622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return values;
1137622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
11383cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
11393cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11403cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CustomDataRowHandler extends DataRowHandler {
11413cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CustomDataRowHandler(String mimetype) {
11433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
11443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
11453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
11463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class StructuredNameRowHandler extends DataRowHandler {
1148622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final NameSplitter mSplitter;
11493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1150622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public StructuredNameRowHandler(NameSplitter splitter) {
11513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(StructuredName.CONTENT_ITEM_TYPE);
1152622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            mSplitter = splitter;
11533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
11543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
11565ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1157622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredNameComponents(values, values);
115814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
115914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
116014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1161f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            String name = values.getAsString(StructuredName.DISPLAY_NAME);
1162d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov            Integer fullNameStyle = values.getAsInteger(StructuredName.FULL_NAME_STYLE);
1163d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov            insertNameLookupForStructuredName(rawContactId, dataId, name,
116451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                    fullNameStyle != null
116551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                            ? mNameSplitter.getAdjustedFullNameStyle(fullNameStyle)
116651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                            : FullNameStyle.UNDEFINED);
116748786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            insertNameLookupForPhoneticName(rawContactId, dataId, values);
116825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1169813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
117014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
117114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
117214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
117314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
1174813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1175f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1176622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1177622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1178cabac02a2416b495e030654accffcbb5ae526678Dmitri Plotnikov
1179622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1180813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (augmented == null) {  // No change
1181813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1182813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1183813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1184622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredNameComponents(augmented, values);
118514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1186f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
11877ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov            if (values.containsKey(StructuredName.DISPLAY_NAME) ||
11887ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                    values.containsKey(StructuredName.PHONETIC_FAMILY_NAME) ||
11897ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                    values.containsKey(StructuredName.PHONETIC_MIDDLE_NAME) ||
11907ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                    values.containsKey(StructuredName.PHONETIC_GIVEN_NAME)) {
11917ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                augmented.putAll(values);
11927ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                String name = augmented.getAsString(StructuredName.DISPLAY_NAME);
1193f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                deleteNameLookup(dataId);
11947ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                Integer fullNameStyle = augmented.getAsInteger(StructuredName.FULL_NAME_STYLE);
1195d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                insertNameLookupForStructuredName(rawContactId, dataId, name,
119651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                        fullNameStyle != null
119751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                                ? mNameSplitter.getAdjustedFullNameStyle(fullNameStyle)
119851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                                : FullNameStyle.UNDEFINED);
11997ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                insertNameLookupForPhoneticName(rawContactId, dataId, augmented);
120014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            }
120125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1202813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
1203813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
120414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
120514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
120614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
120714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
120814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
120914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
121014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
121114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
121214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1213f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
121425abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1215813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
121614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
12173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
1220622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Specific list of structured fields.
12213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
1222622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final String[] STRUCTURED_FIELDS = new String[] {
1223622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredName.PREFIX, StructuredName.GIVEN_NAME, StructuredName.MIDDLE_NAME,
1224622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredName.FAMILY_NAME, StructuredName.SUFFIX
1225622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        };
12263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1227622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1228622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Parses the supplied display name, but only if the incoming values do
1229622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * not already contain structured name parts. Also, if the display name
1230622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * is not provided, generate one by concatenating first name and last
1231622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * name.
1232622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
12337a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        public void fixStructuredNameComponents(ContentValues augmented, ContentValues update) {
123467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final String unstruct = update.getAsString(StructuredName.DISPLAY_NAME);
1235622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
123667c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
123767c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
1238622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1239622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (touchedUnstruct && !touchedStruct) {
12408c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
1241622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                mSplitter.split(name, unstruct);
1242622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                name.toValues(update);
124367c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            } else if (!touchedUnstruct
124467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                    && (touchedStruct || areAnySpecified(update, STRUCTURED_FIELDS))) {
124567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // We need to update the display name when any structured components
124667c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // are specified, even when they are null, which is why we are checking
124767c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // areAnySpecified.  The touchedStruct in the condition is an optimization:
124867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // if there are non-null values, we know for a fact that some values are present.
12498c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
1250622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                name.fromValues(augmented);
12514cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                // As the name could be changed, let's guess the name style again.
12524cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                name.fullNameStyle = FullNameStyle.UNDEFINED;
12534cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                mSplitter.guessNameStyle(name);
1254ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao                int unadjustedFullNameStyle = name.fullNameStyle;
1255ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao                name.fullNameStyle = mSplitter.getAdjustedFullNameStyle(name.fullNameStyle);
12565dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                final String joined = mSplitter.join(name, true);
1257622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                update.put(StructuredName.DISPLAY_NAME, joined);
12585dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
1259ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao                update.put(StructuredName.FULL_NAME_STYLE, unadjustedFullNameStyle);
12605dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                update.put(StructuredName.PHONETIC_NAME_STYLE, name.phoneticNameStyle);
12614cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao            } else if (touchedUnstruct && touchedStruct){
1262d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                if (!update.containsKey(StructuredName.FULL_NAME_STYLE)) {
1263d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                    update.put(StructuredName.FULL_NAME_STYLE,
1264d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                            mSplitter.guessFullNameStyle(unstruct));
12654cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                }
1266d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                if (!update.containsKey(StructuredName.PHONETIC_NAME_STYLE)) {
1267d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                    update.put(StructuredName.PHONETIC_NAME_STYLE,
1268d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                            mSplitter.guessPhoneticNameStyle(unstruct));
12694cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                }
1270622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
1271622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1272622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    }
1273622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1274622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    public class StructuredPostalRowHandler extends DataRowHandler {
1275622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private PostalSplitter mSplitter;
1276622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1277622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public StructuredPostalRowHandler(PostalSplitter splitter) {
1278622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            super(StructuredPostal.CONTENT_ITEM_TYPE);
1279622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            mSplitter = splitter;
1280622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1281622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1282622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1283622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1284622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredPostalComponents(values, values);
1285622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return super.insert(db, rawContactId, values);
1286622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1287622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1288622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1289813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1290f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1291622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1292622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1293813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (augmented == null) {    // No change
1294813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1295813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1296813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1297622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredPostalComponents(augmented, values);
1298f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
1299813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
1300622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1301622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1302622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1303622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Specific list of structured fields.
1304622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1305622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final String[] STRUCTURED_FIELDS = new String[] {
1306622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.STREET, StructuredPostal.POBOX, StructuredPostal.NEIGHBORHOOD,
1307622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.CITY, StructuredPostal.REGION, StructuredPostal.POSTCODE,
1308622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.COUNTRY,
1309622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        };
1310622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1311622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1312622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Prepares the given {@link StructuredPostal} row, building
1313622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * {@link StructuredPostal#FORMATTED_ADDRESS} to match the structured
1314622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * values when missing. When structured components are missing, the
1315622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * unstructured value is assigned to {@link StructuredPostal#STREET}.
1316622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1317622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void fixStructuredPostalComponents(ContentValues augmented, ContentValues update) {
131867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final String unstruct = update.getAsString(StructuredPostal.FORMATTED_ADDRESS);
131967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov
132067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
132167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
1322622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1323622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final PostalSplitter.Postal postal = new PostalSplitter.Postal();
1324622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1325622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (touchedUnstruct && !touchedStruct) {
1326622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                mSplitter.split(postal, unstruct);
1327622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                postal.toValues(update);
132867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            } else if (!touchedUnstruct
132967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                    && (touchedStruct || areAnySpecified(update, STRUCTURED_FIELDS))) {
133067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // See comment in
1331622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                postal.fromValues(augmented);
1332622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                final String joined = mSplitter.join(postal);
1333622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                update.put(StructuredPostal.FORMATTED_ADDRESS, joined);
13343cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
13353cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
13363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
13373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13383cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CommonDataRowHandler extends DataRowHandler {
13393cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13403cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mTypeColumn;
13413cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mLabelColumn;
13423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CommonDataRowHandler(String mimetype, String typeColumn, String labelColumn) {
13443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
13453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mTypeColumn = typeColumn;
13463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mLabelColumn = labelColumn;
13473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
13483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
13505ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1351622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            enforceTypeAndLabel(values, values);
1352622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return super.insert(db, rawContactId, values);
1353622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
13543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1355622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1356813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1357f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1358622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1359622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1360813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (augmented == null) {        // No change
1361813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1362813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1363622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            enforceTypeAndLabel(augmented, values);
1364813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return super.update(db, values, c, callerIsSyncAdapter);
1365622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
13663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1367622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1368622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * If the given {@link ContentValues} defines {@link #mTypeColumn},
1369622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * enforce that {@link #mLabelColumn} only appears when type is
1370622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * {@link BaseTypes#TYPE_CUSTOM}. Exception is thrown otherwise.
1371622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1372622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void enforceTypeAndLabel(ContentValues augmented, ContentValues update) {
1373622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean hasType = !TextUtils.isEmpty(augmented.getAsString(mTypeColumn));
1374622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean hasLabel = !TextUtils.isEmpty(augmented.getAsString(mLabelColumn));
13753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1376622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (hasLabel && !hasType) {
1377622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                // When label exists, assert that some type is defined
1378622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                throw new IllegalArgumentException(mTypeColumn + " must be specified when "
1379622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        + mLabelColumn + " is defined.");
1380622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
13813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
13823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
13833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class OrganizationDataRowHandler extends CommonDataRowHandler {
13853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public OrganizationDataRowHandler() {
13873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Organization.CONTENT_ITEM_TYPE, Organization.TYPE, Organization.LABEL);
13883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
13893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
13915ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1392a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String company = values.getAsString(Organization.COMPANY);
1393a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String title = values.getAsString(Organization.TITLE);
1394a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
1395a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            long dataId = super.insert(db, rawContactId, values);
1396a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
139725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1398a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookupForOrganization(rawContactId, dataId, company, title);
1399a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            return dataId;
14003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
14013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
14023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
1403813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1404f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1405813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1406813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1407813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
140814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
140931168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov            boolean containsCompany = values.containsKey(Organization.COMPANY);
141031168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov            boolean containsTitle = values.containsKey(Organization.TITLE);
141131168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov            if (containsCompany || containsTitle) {
1412813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long dataId = c.getLong(DataUpdateQuery._ID);
1413813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1414813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
141531168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                String company;
141631168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
141731168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                if (containsCompany) {
141831168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    company = values.getAsString(Organization.COMPANY);
141931168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                } else {
142031168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(dataId);
142131168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    company = DatabaseUtils.stringForQuery(db,
142231168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            "SELECT " + Organization.COMPANY +
142331168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            " FROM " + Tables.DATA +
142431168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            " WHERE " + Data._ID + "=?", mSelectionArgs1);
142531168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                }
142631168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
142731168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                String title;
142831168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                if (containsTitle) {
142931168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    title = values.getAsString(Organization.TITLE);
143031168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                } else {
143131168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(dataId);
143231168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    title = DatabaseUtils.stringForQuery(db,
143331168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            "SELECT " + Organization.TITLE +
143431168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            " FROM " + Tables.DATA +
143531168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            " WHERE " + Data._ID + "=?", mSelectionArgs1);
143631168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                }
143731168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
143831168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                deleteNameLookup(dataId);
143931168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                insertNameLookupForOrganization(rawContactId, dataId, company, title);
144031168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
144131168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
144231168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov            }
1443813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
144414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
144514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
144614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
144714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
1448a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            long dataId = c.getLong(DataUpdateQuery._ID);
144914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
145014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
145114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
145225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1453a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            deleteNameLookup(dataId);
145414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
145514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
145614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
145714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
14583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
14593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
14603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_WORK: return 0;
14613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_CUSTOM: return 1;
14623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_OTHER: return 2;
14633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
14643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
14653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
14663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
14673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1468e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    public class EmailDataRowHandler extends CommonDataRowHandler {
1469e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1470e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        public EmailDataRowHandler() {
1471e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            super(Email.CONTENT_ITEM_TYPE, Email.TYPE, Email.LABEL);
1472e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1473e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1474e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
14755ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1476813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            String email = values.getAsString(Email.DATA);
147714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
147814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
147914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
148025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1481813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            String address = insertNameLookupForEmail(rawContactId, dataId, email);
1482813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (address != null) {
1483813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                triggerAggregation(rawContactId);
1484813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
148514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
148614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
148714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
148814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
1489813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1490f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1491813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1492813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1493813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
149414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1495b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov            if (values.containsKey(Email.DATA)) {
1496813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long dataId = c.getLong(DataUpdateQuery._ID);
1497813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1498813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1499b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                String address = values.getAsString(Email.DATA);
1500b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                deleteNameLookup(dataId);
1501b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                insertNameLookupForEmail(rawContactId, dataId, address);
1502b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
1503813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                triggerAggregation(rawContactId);
1504b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov            }
1505813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1506813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
150714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
150814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
150914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
151014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
151114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
151214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
151314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
151414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
151514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1516f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
151725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1518813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
151914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
1520e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1521e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1522e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
1523e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
1524e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            switch (type) {
1525e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_HOME: return 0;
1526e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_WORK: return 1;
1527e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_CUSTOM: return 2;
1528e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_OTHER: return 3;
1529e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                default: return 1000;
1530e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
1531e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1532e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    }
1533e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
153414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    public class NicknameDataRowHandler extends CommonDataRowHandler {
153514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
153614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public NicknameDataRowHandler() {
153714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            super(Nickname.CONTENT_ITEM_TYPE, Nickname.TYPE, Nickname.LABEL);
153814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
153914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
154014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
154114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
154214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String nickname = values.getAsString(Nickname.NAME);
154314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
154414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
154514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1546813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!TextUtils.isEmpty(nickname)) {
1547813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
1548813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                insertNameLookupForNickname(rawContactId, dataId, nickname);
1549813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                triggerAggregation(rawContactId);
1550813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
155114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
155214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
155314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
155414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
1555813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1556f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
155714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
155814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
155914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1560813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1561813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1562813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
156314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1564b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov            if (values.containsKey(Nickname.NAME)) {
1565b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                String nickname = values.getAsString(Nickname.NAME);
1566b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                deleteNameLookup(dataId);
1567b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                insertNameLookupForNickname(rawContactId, dataId, nickname);
1568b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
1569813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                triggerAggregation(rawContactId);
1570b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov            }
1571813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1572813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
157314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
157414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
157514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
157614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
157714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
157814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
157914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
158014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
158114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1582f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
158325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1584813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
158514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
158614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
158714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    }
158814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
15893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class PhoneDataRowHandler extends CommonDataRowHandler {
15903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
15913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public PhoneDataRowHandler() {
15923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Phone.CONTENT_ITEM_TYPE, Phone.TYPE, Phone.LABEL);
15933cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
15943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
15953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
15965ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
15970b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            long dataId;
15980b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
15990b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String number = values.getAsString(Phone.NUMBER);
16006206cd7e15b1fe63b72cc9ba32a4d84c764963ceDmitri Plotnikov
1601d015a8321fb9dcbfa96becb909145dfcce3da608Bai Tao                String numberE164 =
1602d015a8321fb9dcbfa96becb909145dfcce3da608Bai Tao                        PhoneNumberUtils.formatNumberToE164(number, getCurrentCountryIso());
1603892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                if (numberE164 != null) {
1604892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    values.put(PhoneColumns.NORMALIZED_NUMBER, numberE164);
1605892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                }
16060b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                dataId = super.insert(db, rawContactId, values);
1607653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1608892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                updatePhoneLookup(db, rawContactId, dataId, number, numberE164);
1609285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateHasPhoneNumber(db, rawContactId);
161025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
1611892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                if (numberE164 != null) {
1612813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                    triggerAggregation(rawContactId);
1613813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                }
16140b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            } else {
16150b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                dataId = super.insert(db, rawContactId, values);
16160b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            }
1617653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return dataId;
1618653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1619653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1620653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1621813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1622f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1623813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            String number = null;
1624813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            String normalizedNumber = null;
1625892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            String numberE164 = null;
16260b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
1627813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                number = values.getAsString(Phone.NUMBER);
1628892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                if (number != null) {
1629892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    numberE164 =
1630892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        PhoneNumberUtils.formatNumberToE164(number, getCurrentCountryIso());
1631892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                }
1632892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                if (numberE164 != null) {
1633892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    values.put(PhoneColumns.NORMALIZED_NUMBER, numberE164);
1634892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                }
1635813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1636653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1637813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1638813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1639813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1640653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1641813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
1642813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long dataId = c.getLong(DataUpdateQuery._ID);
1643813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1644892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                updatePhoneLookup(db, rawContactId, dataId, number, numberE164);
1645285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateHasPhoneNumber(db, rawContactId);
164625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
1647813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                triggerAggregation(rawContactId);
16480b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            }
1649813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
165014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
165114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
165214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
165314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
165414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
165514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
165614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
165714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
165814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
165914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            updatePhoneLookup(db, rawContactId, dataId, null, null);
1660285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            mContactAggregator.updateHasPhoneNumber(db, rawContactId);
166125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1662813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
166314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
1664653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1665653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1666653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private void updatePhoneLookup(SQLiteDatabase db, long rawContactId, long dataId,
1667892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                String number, String numberE164) {
1668892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(dataId);
1669892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            db.delete(Tables.PHONE_LOOKUP, PhoneLookupColumns.DATA_ID + "=?", mSelectionArgs1);
1670e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (number != null) {
1671892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                String normalizedNumber = PhoneNumberUtils.normalizeNumber(number);
1672892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                if (!TextUtils.isEmpty(normalizedNumber)) {
1673892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    ContentValues phoneValues = new ContentValues();
1674892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    phoneValues.put(PhoneLookupColumns.RAW_CONTACT_ID, rawContactId);
1675892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    phoneValues.put(PhoneLookupColumns.DATA_ID, dataId);
1676892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    phoneValues.put(PhoneLookupColumns.NORMALIZED_NUMBER, normalizedNumber);
1677892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    phoneValues.put(PhoneLookupColumns.MIN_MATCH,
1678892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                            PhoneNumberUtils.toCallerIDMinMatch(normalizedNumber));
1679892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    db.insert(Tables.PHONE_LOOKUP, null, phoneValues);
1680892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov
1681892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (numberE164 != null && !numberE164.equals(normalizedNumber)) {
1682892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        phoneValues.put(PhoneLookupColumns.NORMALIZED_NUMBER, numberE164);
1683892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        phoneValues.put(PhoneLookupColumns.MIN_MATCH,
1684892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                PhoneNumberUtils.toCallerIDMinMatch(numberE164));
1685892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        db.insert(Tables.PHONE_LOOKUP, null, phoneValues);
1686892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    }
1687892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                }
1688e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
16893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
16903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
16913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
16923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
16933cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
16943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_MOBILE: return 0;
16953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_WORK: return 1;
16963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_HOME: return 2;
16973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_PAGER: return 3;
16983cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_CUSTOM: return 4;
16993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_OTHER: return 5;
17003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_WORK: return 6;
17013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_HOME: return 7;
17023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
17033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
17043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
17053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
17063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1707653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    public class GroupMembershipRowHandler extends DataRowHandler {
1708653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1709dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        private static final String SELECTION_RAW_CONTACT_ID = RawContacts._ID + "=?";
1710dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1711dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        private static final String QUERY_COUNT_FAVORITES_GROUP_MEMBERSHIPS_BY_RAW_CONTACT_ID =
1712dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                "SELECT COUNT(*) FROM " + Tables.DATA + " LEFT OUTER JOIN " + Tables .GROUPS
1713dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + " ON " + Tables.DATA + "." + GroupMembership.GROUP_ROW_ID
1714dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + "=" + GroupsColumns.CONCRETE_ID
1715dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + " WHERE " + DataColumns.MIMETYPE_ID + "=?"
1716dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + " AND " + Tables.DATA + "." + GroupMembership.RAW_CONTACT_ID + "=?"
1717dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + " AND " + Groups.FAVORITES + "!=0";
1718dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1719653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public GroupMembershipRowHandler() {
1720653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            super(GroupMembership.CONTENT_ITEM_TYPE);
1721653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1722653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1723653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1724653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1725653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            resolveGroupSourceIdInValues(rawContactId, db, values, true);
17260be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
1727dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (hasFavoritesGroupMembership(db, rawContactId)) {
1728dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContactsStar(db, rawContactId, true /* starred */);
1729dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
17300be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
17310be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            return dataId;
1732653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1733653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1734653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1735813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1736f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
173714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1738dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean wasStarred = hasFavoritesGroupMembership(db, rawContactId);
1739653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            resolveGroupSourceIdInValues(rawContactId, db, values, false);
1740813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1741813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1742813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1743dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean isStarred = hasFavoritesGroupMembership(db, rawContactId);
1744dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (wasStarred != isStarred) {
1745dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContactsStar(db, rawContactId, isStarred);
1746dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
17470be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
1748813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
17490be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        }
17500be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
1751dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        private void updateRawContactsStar(SQLiteDatabase db, long rawContactId, boolean starred) {
1752dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            ContentValues rawContactValues = new ContentValues();
1753dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            rawContactValues.put(RawContacts.STARRED, starred ? 1 : 0);
1754dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (db.update(Tables.RAW_CONTACTS, rawContactValues, SELECTION_RAW_CONTACT_ID,
1755dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[]{Long.toString(rawContactId)}) > 0) {
1756dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                mContactAggregator.updateStarred(rawContactId);
1757dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
1758dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
1759dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1760dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        private boolean hasFavoritesGroupMembership(SQLiteDatabase db, long rawContactId) {
1761dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            final long groupMembershipMimetypeId = mDbHelper
1762dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
1763dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean isStarred = 0 < DatabaseUtils
1764dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    .longForQuery(db, QUERY_COUNT_FAVORITES_GROUP_MEMBERSHIPS_BY_RAW_CONTACT_ID,
1765dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[]{Long.toString(groupMembershipMimetypeId), Long.toString(rawContactId)});
1766dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return isStarred;
1767dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
1768dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
17690be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        @Override
17700be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
17710be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
1772dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean wasStarred = hasFavoritesGroupMembership(db, rawContactId);
17730be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            int count = super.delete(db, c);
1774dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean isStarred = hasFavoritesGroupMembership(db, rawContactId);
1775dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (wasStarred && !isStarred) {
1776dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContactsStar(db, rawContactId, false /* starred */);
1777dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
17780be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
17790be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            return count;
17800be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        }
17810be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
17820be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        private void updateVisibility(long rawContactId) {
1783b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            long contactId = mDbHelper.getContactId(rawContactId);
17840be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            if (contactId != 0) {
1785b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                mDbHelper.updateContactVisible(contactId);
17860be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            }
1787653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1788653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1789653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private void resolveGroupSourceIdInValues(long rawContactId, SQLiteDatabase db,
1790653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                ContentValues values, boolean isInsert) {
1791653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            boolean containsGroupSourceId = values.containsKey(GroupMembership.GROUP_SOURCE_ID);
1792653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            boolean containsGroupId = values.containsKey(GroupMembership.GROUP_ROW_ID);
1793653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (containsGroupSourceId && containsGroupId) {
1794653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                throw new IllegalArgumentException(
1795653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        "you are not allowed to set both the GroupMembership.GROUP_SOURCE_ID "
1796653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                + "and GroupMembership.GROUP_ROW_ID");
1797653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1798653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1799653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (!containsGroupSourceId && !containsGroupId) {
1800653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                if (isInsert) {
1801653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                    throw new IllegalArgumentException(
1802653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                            "you must set exactly one of GroupMembership.GROUP_SOURCE_ID "
1803653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                    + "and GroupMembership.GROUP_ROW_ID");
1804653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                } else {
1805653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                    return;
1806653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                }
1807653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1808653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1809653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (containsGroupSourceId) {
1810653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                final String sourceId = values.getAsString(GroupMembership.GROUP_SOURCE_ID);
1811ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                final long groupId = getOrMakeGroup(db, rawContactId, sourceId,
1812ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        mInsertedRawContacts.get(rawContactId));
1813653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(GroupMembership.GROUP_SOURCE_ID);
1814653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.put(GroupMembership.GROUP_ROW_ID, groupId);
1815653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1816653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1817653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    }
1818653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1819a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov    public class PhotoDataRowHandler extends DataRowHandler {
1820a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1821a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public PhotoDataRowHandler() {
1822a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            super(Photo.CONTENT_ITEM_TYPE);
1823a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1824a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1825a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1826a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1827a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
1828285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (!isNewRawContact(rawContactId)) {
1829285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updatePhotoId(db, rawContactId);
1830285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
1831a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return dataId;
1832a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1833a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1834a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1835813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1836f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1837a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1838813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1839813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1840813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1841813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1842a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            mContactAggregator.updatePhotoId(db, rawContactId);
1843813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
1844a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1845a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1846a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1847a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
1848a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
1849a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            int count = super.delete(db, c);
1850a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            mContactAggregator.updatePhotoId(db, rawContactId);
1851a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return count;
1852a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1853a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov    }
1854a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1855ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    /**
1856ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * An entry in group id cache. It maps the combination of (account type, account name
1857ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * and source id) to group row id.
1858ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     */
1859ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    public class GroupIdCacheEntry {
1860ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType;
1861ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName;
1862ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String sourceId;
1863ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        long groupId;
1864ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    }
1865a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
18663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private HashMap<String, DataRowHandler> mDataRowHandlers;
1867b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private ContactsDatabaseHelper mDbHelper;
186831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
18694097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
1870f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private NameLookupBuilder mNameLookupBuilder;
1871315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
1872622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private PostalSplitter mPostalSplitter;
1873622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1874ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    // We don't need a soft cache for groups - the assumption is that there will only
1875ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    // be a small number of contact groups. The cache is keyed off source id.  The value
1876ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    // is a list of groups with this group id.
1877ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    private HashMap<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache = Maps.newHashMap();
1878ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
187972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    private ContactDirectoryManager mContactDirectoryManager;
1880622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private ContactAggregator mContactAggregator;
1881f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
1882a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    private GlobalSearchSupport mGlobalSearchSupport;
1883d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov    private CommonNicknameCache mCommonNicknameCache;
1884a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
188520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private ContentValues mValues = new ContentValues();
18861129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private CharArrayBuffer mCharArrayBuffer = new CharArrayBuffer(128);
18875dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    private NameSplitter.Name mName = new NameSplitter.Name();
188873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    private HashMap<String, Boolean> mAccountWritability = Maps.newHashMap();
188920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
189009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private int mProviderStatus = ProviderStatus.STATUS_NORMAL;
18913826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private boolean mProviderStatusUpdateNeeded;
189209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private long mEstimatedStorageRequirement = 0;
1893ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov    private volatile CountDownLatch mAccessLatch;
189473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
1895ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    private HashMap<Long, Account> mInsertedRawContacts = Maps.newHashMap();
1896b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private HashSet<Long> mUpdatedRawContacts = Sets.newHashSet();
1897a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private HashSet<Long> mDirtyRawContacts = Sets.newHashSet();
1898b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private HashMap<Long, Object> mUpdatedSyncStates = Maps.newHashMap();
1899de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov
19001a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey    private boolean mVisibleTouched = false;
19011a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
190281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    private boolean mSyncToNetwork;
190381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
19044cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    private Locale mCurrentLocale;
19053826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private int mContactsAccountCount;
1906d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov
19072530512f639c4979fd7371c7dd25dd67e8118124Bai Tao    private CountryMonitor mCountryMonitor;
190873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
19094f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
19104f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
1911de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        super.onCreate();
1912ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        try {
1913ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return initialize();
1914ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        } catch (RuntimeException e) {
1915ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            Log.e(TAG, "Cannot start provider", e);
1916ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return false;
1917ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        }
1918ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    }
191935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1920ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    private boolean initialize() {
1921de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final Context context = getContext();
19222530512f639c4979fd7371c7dd25dd67e8118124Bai Tao        mCountryMonitor = CountryMonitor.getInstance(context);
1923b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper = (ContactsDatabaseHelper)getDatabaseHelper();
192472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        mContactDirectoryManager = new ContactDirectoryManager(this);
1925a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mGlobalSearchSupport = new GlobalSearchSupport(this);
1926b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mLegacyApiSupport = new LegacyApiSupport(context, mDbHelper, this, mGlobalSearchSupport);
1927b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mDb = mDbHelper.getWritableDatabase();
1928653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
192951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        initForDefaultLocale();
1930d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov
1931b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mSetPrimaryStatement = mDb.compileStatement(
1932653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "UPDATE " + Tables.DATA +
1933653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " SET " + Data.IS_PRIMARY + "=(_id=?)" +
1934653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " WHERE " + DataColumns.MIMETYPE_ID + "=?" +
1935653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "   AND " + Data.RAW_CONTACT_ID + "=?");
1936653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1937b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mSetSuperPrimaryStatement = mDb.compileStatement(
1938653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "UPDATE " + Tables.DATA +
1939653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " SET " + Data.IS_SUPER_PRIMARY + "=(" + Data._ID + "=?)" +
1940653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " WHERE " + DataColumns.MIMETYPE_ID + "=?" +
1941653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "   AND " + Data.RAW_CONTACT_ID + " IN (" +
1942653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        "SELECT " + RawContacts._ID +
1943653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        " FROM " + Tables.RAW_CONTACTS +
1944653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        " WHERE " + RawContacts.CONTACT_ID + " =(" +
1945653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                "SELECT " + RawContacts.CONTACT_ID +
1946653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                " FROM " + Tables.RAW_CONTACTS +
1947653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                " WHERE " + RawContacts._ID + "=?))");
1948653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1949b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mRawContactDisplayNameUpdate = mDb.compileStatement(
195025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                "UPDATE " + Tables.RAW_CONTACTS +
19515dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                " SET " +
19525dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.DISPLAY_NAME_SOURCE + "=?," +
19535dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.DISPLAY_NAME_PRIMARY + "=?," +
19545dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.DISPLAY_NAME_ALTERNATIVE + "=?," +
19555dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.PHONETIC_NAME + "=?," +
19565dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.PHONETIC_NAME_STYLE + "=?," +
19575dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.SORT_KEY_PRIMARY + "=?," +
19585dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.SORT_KEY_ALTERNATIVE + "=?" +
195925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                " WHERE " + RawContacts._ID + "=?");
19603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1961b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mNameLookupInsert = mDb.compileStatement("INSERT OR IGNORE INTO " + Tables.NAME_LOOKUP + "("
1962f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.RAW_CONTACT_ID + "," + NameLookupColumns.DATA_ID + ","
1963f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.NAME_TYPE + "," + NameLookupColumns.NORMALIZED_NAME
1964f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + ") VALUES (?,?,?,?)");
1965b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mNameLookupDelete = mDb.compileStatement("DELETE FROM " + Tables.NAME_LOOKUP + " WHERE "
1966f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.DATA_ID + "=?");
1967f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
1968b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mStatusUpdateInsert = mDb.compileStatement(
1969a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "INSERT INTO " + Tables.STATUS_UPDATES + "("
1970a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + StatusUpdatesColumns.DATA_ID + ", "
19710a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + ","
19720a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_RES_PACKAGE + ","
19730a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + ","
19740a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + ")" +
19750a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " VALUES (?,?,?,?,?)");
1976a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1977b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mStatusUpdateReplace = mDb.compileStatement(
1978a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "INSERT OR REPLACE INTO " + Tables.STATUS_UPDATES + "("
1979a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + StatusUpdatesColumns.DATA_ID + ", "
19800a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_TIMESTAMP + ","
19810a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + ","
19820a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_RES_PACKAGE + ","
19830a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + ","
19840a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + ")" +
19850a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " VALUES (?,?,?,?,?,?)");
1986a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1987b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mStatusUpdateAutoTimestamp = mDb.compileStatement(
1988a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "UPDATE " + Tables.STATUS_UPDATES +
19890a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " SET " + StatusUpdates.STATUS_TIMESTAMP + "=?,"
19900a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + "=?" +
1991a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?"
19920a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + " AND " + StatusUpdates.STATUS + "!=?");
19930a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
1994b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mStatusAttributionUpdate = mDb.compileStatement(
19950a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                "UPDATE " + Tables.STATUS_UPDATES +
19960a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " SET " + StatusUpdates.STATUS_RES_PACKAGE + "=?,"
19970a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + "=?,"
19980a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + "=?" +
19990a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?");
2000a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2001b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mStatusUpdateDelete = mDb.compileStatement(
2002a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "DELETE FROM " + Tables.STATUS_UPDATES +
2003a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?");
2004a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2005f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov        // When setting NAME_VERIFIED to 1 on a raw contact, reset it to 0
2006f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov        // on all other raw contacts in the same aggregate
2007b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mResetNameVerifiedForOtherRawContacts = mDb.compileStatement(
2008f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                "UPDATE " + Tables.RAW_CONTACTS +
2009f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                " SET " + RawContacts.NAME_VERIFIED + "=0" +
2010f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                " WHERE " + RawContacts.CONTACT_ID + "=(" +
2011f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                        "SELECT " + RawContacts.CONTACT_ID +
2012f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                        " FROM " + Tables.RAW_CONTACTS +
2013f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                        " WHERE " + RawContacts._ID + "=?)" +
2014f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                " AND " + RawContacts._ID + "!=?");
2015f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
2016f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mMimeTypeIdEmail = mDbHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE);
2017f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mMimeTypeIdIm = mDbHelper.getMimeTypeId(Im.CONTENT_ITEM_TYPE);
20181129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdStructuredName = mDbHelper.getMimeTypeId(StructuredName.CONTENT_ITEM_TYPE);
20191129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdOrganization = mDbHelper.getMimeTypeId(Organization.CONTENT_ITEM_TYPE);
20201129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdNickname = mDbHelper.getMimeTypeId(Nickname.CONTENT_ITEM_TYPE);
20211129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdPhone = mDbHelper.getMimeTypeId(Phone.CONTENT_ITEM_TYPE);
202204b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov
20233826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        updateAccounts();
2024bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
202565ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov        if (isLegacyContactImportNeeded()) {
202665ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov            importLegacyContactsAsync();
202780952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov        } else {
202880952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov            verifyLocale();
202965ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov        }
203065ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov
203172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        startContactDirectoryManager();
203272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
20332a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov        if (isAggregationUpgradeNeeded()) {
20342a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov            upgradeAggregationAlgorithm();
20352a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov        }
20362a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov
20373826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        updateProviderStatus();
20383826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
2039b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        return (mDb != null);
20404f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
20414f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
20422530512f639c4979fd7371c7dd25dd67e8118124Bai Tao    protected String getCurrentCountryIso() {
20432530512f639c4979fd7371c7dd25dd67e8118124Bai Tao        return mCountryMonitor.getCountryIso();
2044892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov    }
2045892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov
2046ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao    private void initDataRowHandlers() {
2047ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers = new HashMap<String, DataRowHandler>();
2048ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao
2049ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(Email.CONTENT_ITEM_TYPE, new EmailDataRowHandler());
2050ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(Im.CONTENT_ITEM_TYPE,
2051ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao              new CommonDataRowHandler(Im.CONTENT_ITEM_TYPE, Im.TYPE, Im.LABEL));
2052ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new CommonDataRowHandler(
2053ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao              StructuredPostal.CONTENT_ITEM_TYPE, StructuredPostal.TYPE, StructuredPostal.LABEL));
2054ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(Organization.CONTENT_ITEM_TYPE, new OrganizationDataRowHandler());
2055ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(Phone.CONTENT_ITEM_TYPE, new PhoneDataRowHandler());
2056ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new NicknameDataRowHandler());
2057ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(StructuredName.CONTENT_ITEM_TYPE,
2058ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao              new StructuredNameRowHandler(mNameSplitter));
2059ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(StructuredPostal.CONTENT_ITEM_TYPE,
2060ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao              new StructuredPostalRowHandler(mPostalSplitter));
2061ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(GroupMembership.CONTENT_ITEM_TYPE, new GroupMembershipRowHandler());
2062ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(Photo.CONTENT_ITEM_TYPE, new PhotoDataRowHandler());
2063ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao    }
206472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
206551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    /**
2066767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov     * Visible for testing.
2067767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov     */
2068767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    /* package */ PhotoPriorityResolver createPhotoPriorityResolver(Context context) {
2069767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov        return new PhotoPriorityResolver(context);
2070767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    }
2071767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov
2072767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    /**
207351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * (Re)allocates all locale-sensitive structures.
207451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
207504b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov    private void initForDefaultLocale() {
20764cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mCurrentLocale = getLocale();
207704b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov        mNameSplitter = mDbHelper.createNameSplitter();
20784cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
20794cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mPostalSplitter = new PostalSplitter(mCurrentLocale);
208051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        mCommonNicknameCache = new CommonNicknameCache(mDbHelper.getReadableDatabase());
2081cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao        ContactLocaleUtils.getIntance().setLocale(mCurrentLocale);
20825b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator = new ContactAggregator(this, mDbHelper,
20835b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                createPhotoPriorityResolver(getContext()), mNameSplitter, mCommonNicknameCache);
20845b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
20855b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
2086ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao        initDataRowHandlers();
20874cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
20884cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao
208953fac8f99f3884c372c907a76766d27fa9e1d95fDmitri Plotnikov    public void onLocaleChanged() {
20903826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
20913826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
20924f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov            return;
20934f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov        }
20944f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov
209551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        initForDefaultLocale();
209651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        verifyLocale();
20974cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
209851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
209951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    /**
210051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * Verifies that the contacts database is properly configured for the current locale.
210151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * If not, changes the database locale to the current locale using an asynchronous task.
210251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * This needs to be done asynchronously because the process involves rebuilding
210351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * large data structures (name lookup, sort keys), which can take minutes on
210451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * a large set of contacts.
210551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
210651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    protected void verifyLocale() {
2107f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
2108f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        // The process is already running - postpone the change
2109f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        if (mProviderStatus == ProviderStatus.STATUS_CHANGING_LOCALE) {
2110f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov            return;
2111f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        }
2112f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
211351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
211451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final String providerLocale = prefs.getString(PREF_LOCALE, null);
211551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final Locale currentLocale = mCurrentLocale;
211651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        if (currentLocale.toString().equals(providerLocale)) {
211751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            return;
211851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        }
211951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
212051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        int providerStatus = mProviderStatus;
212151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_CHANGING_LOCALE);
212251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
212351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        AsyncTask<Integer, Void, Void> task = new AsyncTask<Integer, Void, Void>() {
212451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
212551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            int savedProviderStatus;
212651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
212751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            @Override
212851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            protected Void doInBackground(Integer... params) {
212951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                savedProviderStatus = params[0];
213051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                mDbHelper.setLocale(ContactsProvider2.this, currentLocale);
213151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                return null;
213251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            }
213351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
213451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            @Override
213551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            protected void onPostExecute(Void result) {
21369516b6eef76b3504913f5efcadf603969946a3d0Brad Fitzpatrick                prefs.edit().putString(PREF_LOCALE, currentLocale.toString()).apply();
213751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                setProviderStatus(savedProviderStatus);
2138f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
2139f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov                // Recursive invocation, needed to cover the case where locale
2140f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov                // changes once and then changes again before the db upgrade is completed.
2141f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov                verifyLocale();
214251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            }
214351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        };
214451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
214551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        task.execute(providerStatus);
214651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
214751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
21483826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateProviderStatus() {
21493826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
21503826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
21513826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return;
21523826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
21533826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
21543826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mContactsAccountCount == 0
21553826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && DatabaseUtils.queryNumEntries(mDb, Tables.CONTACTS, null) == 0) {
21563826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            setProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS);
21573826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } else {
21583826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            setProviderStatus(ProviderStatus.STATUS_NORMAL);
21593826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
21603826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
21613826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
216231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
2163de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    @Override
2164b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
2165b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        return ContactsDatabaseHelper.getInstance(context);
216631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
216731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
2168013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    /* package */ NameSplitter getNameSplitter() {
2169013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        return mNameSplitter;
2170013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    }
2171013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov
21725dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    /* Visible for testing */
217372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public ContactDirectoryManager getContactDirectoryManager() {
217472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        return mContactDirectoryManager;
217572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
217672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
217772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    /* Visible for testing */
21785dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    protected Locale getLocale() {
21795dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        return Locale.getDefault();
21805dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
21815dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
218272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    /* Visible for testing */
218372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    protected void startContactDirectoryManager() {
218472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        getContactDirectoryManager().start();
218572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
218672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
21873d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    protected boolean isLegacyContactImportNeeded() {
2188b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        int version = Integer.parseInt(mDbHelper.getProperty(PROPERTY_CONTACTS_IMPORTED, "0"));
2189b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        return version < PROPERTY_CONTACTS_IMPORT_VERSION;
21903d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
21913d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2192568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected LegacyContactImporter getLegacyContactImporter() {
2193568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return new LegacyContactImporter(getContext(), this);
2194568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2195568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2196568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
2197568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * Imports legacy contacts in a separate thread.  As long as the import process is running
2198568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * all other access to the contacts is blocked.
2199568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
2200568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    private void importLegacyContactsAsync() {
2201bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Importing legacy contacts");
2202bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADING);
2203bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        if (mAccessLatch == null) {
2204bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            mAccessLatch = new CountDownLatch(1);
2205bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
2206568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2207568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        Thread importThread = new Thread("LegacyContactImport") {
2208568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            @Override
2209568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            public void run() {
221080952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov                final SharedPreferences prefs =
221180952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov                    PreferenceManager.getDefaultSharedPreferences(getContext());
221280952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov                mDbHelper.setLocale(ContactsProvider2.this, mCurrentLocale);
221380952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov                prefs.edit().putString(PREF_LOCALE, mCurrentLocale.toString()).commit();
221480952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov
2215bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                LegacyContactImporter importer = getLegacyContactImporter();
2216bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                if (importLegacyContacts(importer)) {
2217bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    onLegacyContactImportSuccess();
2218bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                } else {
2219bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    onLegacyContactImportFailure();
2220568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                }
2221568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            }
2222568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        };
2223568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2224568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        importThread.start();
2225568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2226568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2227bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
2228bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Unlocks the provider and declares that the import process is complete.
2229bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
2230bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportSuccess() {
2231bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
2232bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)getContext().getSystemService(Context.NOTIFICATION_SERVICE);
2233bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.cancel(LEGACY_IMPORT_FAILED_NOTIFICATION);
2234bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2235b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        // Store a property in the database indicating that the conversion process succeeded
2236b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mDbHelper.setProperty(PROPERTY_CONTACTS_IMPORTED,
2237b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov                String.valueOf(PROPERTY_CONTACTS_IMPORT_VERSION));
2238bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_NORMAL);
2239bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mAccessLatch.countDown();
2240bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mAccessLatch = null;
2241bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Completed import of legacy contacts");
2242bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    }
2243bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2244bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
2245bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Announces the provider status and keeps the provider locked.
2246bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
2247bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportFailure() {
2248bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Context context = getContext();
2249bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
2250bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
2251bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2252bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        // Show a notification
2253bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Notification n = new Notification(android.R.drawable.stat_notify_error,
2254bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_ticker),
2255bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                System.currentTimeMillis());
2256bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.setLatestEventInfo(context,
2257bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_title),
2258bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_text),
2259bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                PendingIntent.getActivity(context, 0, new Intent(Intents.UI.LIST_DEFAULT), 0));
2260bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
2261bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2262bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.notify(LEGACY_IMPORT_FAILED_NOTIFICATION, n);
2263bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2264bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADE_OUT_OF_MEMORY);
2265bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Failed to import legacy contacts");
22663d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
22673d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
22683d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /* Visible for testing */
2269568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
22700e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        boolean aggregatorEnabled = mContactAggregator.isEnabled();
22713d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mContactAggregator.setEnabled(false);
22723d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        try {
2273bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            if (importer.importContacts()) {
2274bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2275bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                // TODO aggregate all newly added raw contacts
2276bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                mContactAggregator.setEnabled(aggregatorEnabled);
2277bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                return true;
2278bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
22793d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } catch (Throwable e) {
22803d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           Log.e(TAG, "Legacy contact import failed", e);
22813d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
2282bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mEstimatedStorageRequirement = importer.getEstimatedStorageRequirement();
2283bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        return false;
22843d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
22853d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2286a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
2287a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
2288a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
2289a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
2290b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper.wipeData();
22913826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatus = ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS;
2292a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
2293a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2294568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
2295568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * While importing and aggregating contacts, this content provider will
2296568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * block all attempts to change contacts data. In particular, it will hold
2297568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * up all contact syncs. As soon as the import process is complete, all
2298568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * processes waiting to write to the provider are unblocked and can proceed
2299568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * to compete for the database transaction monitor.
2300568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
2301568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    private void waitForAccess() {
2302ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        CountDownLatch latch = mAccessLatch;
2303ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        if (latch != null) {
2304ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            while (true) {
2305ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                try {
2306ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    latch.await();
2307ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    mAccessLatch = null;
2308ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    return;
2309ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                } catch (InterruptedException e) {
231081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                    Thread.currentThread().interrupt();
2311ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                }
2312ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            }
2313568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
2314568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2315568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2316568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2317568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
2318568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
2319568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.insert(uri, values);
2320568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2321568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2322568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2323568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
2324bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        if (mAccessLatch != null) {
2325bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // We are stuck trying to upgrade contacts db.  The only update request
2326bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // allowed in this case is an update of provider status, which will trigger
2327bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // an attempt to upgrade contacts again.
2328bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            int match = sUriMatcher.match(uri);
2329bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            if (match == PROVIDER_STATUS && isLegacyContactImportNeeded()) {
2330bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                Integer newStatus = values.getAsInteger(ProviderStatus.STATUS);
2331bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                if (newStatus != null && newStatus == ProviderStatus.STATUS_UPGRADING) {
2332bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    importLegacyContactsAsync();
2333bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 1;
2334bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                } else {
2335bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 0;
2336bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                }
2337bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
2338bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
2339568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
2340568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.update(uri, values, selection, selectionArgs);
2341568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2342568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2343568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2344568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
2345568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
2346568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.delete(uri, selection, selectionArgs);
2347568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2348568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2349568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2350568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
2351568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            throws OperationApplicationException {
2352568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
2353568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.applyBatch(operations);
2354568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2355568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
23564f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2357285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void onBeginTransaction() {
2358bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2359b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "onBeginTransaction");
2360b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2361285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.onBeginTransaction();
23621ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.clearPendingAggregations();
2363b5a4add17815167d20a90645779df34cdf45280dFred Quintana        clearTransactionalChanges();
2364b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2365b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2366b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void clearTransactionalChanges() {
2367285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        mInsertedRawContacts.clear();
2368b5a4add17815167d20a90645779df34cdf45280dFred Quintana        mUpdatedRawContacts.clear();
2369df9db5e99572ce9760eb265683134c1f3293928fFred Quintana        mUpdatedSyncStates.clear();
2370a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        mDirtyRawContacts.clear();
2371285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
2372285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2373285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2374285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void beforeTransactionCommit() {
23751129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
2376bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2377b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "beforeTransactionCommit");
2378b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2379285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.beforeTransactionCommit();
2380b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
23811ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.aggregateInTransaction(mDb);
23821a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (mVisibleTouched) {
23831a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = false;
2384b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.updateAllVisible();
23851a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        }
23863826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
23873826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatusUpdateNeeded) {
23883826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
23893826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatusUpdateNeeded = false;
23903826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
2391b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2392b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2393b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void flushTransactionalChanges() {
2394bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2395b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "flushTransactionChanges");
2396b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
23971129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
239808e42c9c153a60bf2e7c71dd40bf84bb5fc93555Dmitri Plotnikov        for (long rawContactId : mInsertedRawContacts.keySet()) {
2399d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            updateRawContactDisplayName(mDb, rawContactId);
2400d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            mContactAggregator.onRawContactInsert(mDb, rawContactId);
2401285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        }
2402b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2403a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        if (!mDirtyRawContacts.isEmpty()) {
2404a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
2405a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_DIRTY_SQL);
2406a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            appendIds(mSb, mDirtyRawContacts);
2407a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
2408a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
2409a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        }
2410a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
2411b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (!mUpdatedRawContacts.isEmpty()) {
2412a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
2413a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_VERSION_SQL);
2414a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            appendIds(mSb, mUpdatedRawContacts);
2415a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
2416a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
2417b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2418b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2419b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (Map.Entry<Long, Object> entry : mUpdatedSyncStates.entrySet()) {
2420b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long id = entry.getKey();
24219d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            if (mDbHelper.getSyncState().update(mDb, id, entry.getValue()) <= 0) {
24229d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                throw new IllegalStateException(
24239d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                        "unable to update sync state, does it still exist?");
24249d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            }
2425b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2426b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2427b5a4add17815167d20a90645779df34cdf45280dFred Quintana        clearTransactionalChanges();
2428b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2429b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2430a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /**
2431a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * Appends comma separated ids.
2432a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * @param ids Should not be empty
2433a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     */
2434a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private void appendIds(StringBuilder sb, HashSet<Long> ids) {
2435b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long id : ids) {
2436a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            sb.append(id).append(',');
2437b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2438a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
2439a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        sb.setLength(sb.length() - 1); // Yank the last comma
2440285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
2441285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2442285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2443cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    protected void notifyChange() {
244481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        notifyChange(mSyncToNetwork);
244581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = false;
244681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
244781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
244881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    protected void notifyChange(boolean syncToNetwork) {
244981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
245081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                syncToNetwork);
2451cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    }
2452568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
245351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    protected void setProviderStatus(int status) {
24543826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != status) {
24553826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatus = status;
24563826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            getContext().getContentResolver().notifyChange(ProviderStatus.CONTENT_URI, null, false);
24573826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
245851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
245951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
2460285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    private boolean isNewRawContact(long rawContactId) {
2461ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        return mInsertedRawContacts.containsKey(rawContactId);
2462285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
2463285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
24643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private DataRowHandler getDataRowHandler(final String mimeType) {
24653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
24663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
24673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            handler = new CustomDataRowHandler(mimeType);
24683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
24693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
24703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
24713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
24723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
24734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2474de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected Uri insertInTransaction(Uri uri, ContentValues values) {
2475bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
24761129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Log.v(TAG, "insertInTransaction: " + uri + " " + values);
2477b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2478f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2479f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2480f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2481f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2482a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
2483a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
248435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2485a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
248635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2487b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                id = mDbHelper.getSyncState().insert(mDb, values);
248835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
248935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2490d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
2491d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                insertContact(values);
24926bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
24936bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
24946bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
24955ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
2496dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                id = insertRawContact(uri, values, callerIsSyncAdapter);
2497f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2498a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2499a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2500a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
25015ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
25025ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(1));
2503f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2504f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2505a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2506a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2507a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2508a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case DATA: {
2509f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2510f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2511a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2512a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2513a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2514ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
2515f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                id = insertGroup(uri, values, callerIsSyncAdapter);
2516f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2517ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2518ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2519ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2520eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
25215aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertSettings(uri, values);
252243880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2523eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
2524eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2525eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
252682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
252782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                id = insertStatusUpdate(values);
25281f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
25291f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
25301f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2531a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
253281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
2533f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
2534a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2535a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
25367e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
25377e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
25387e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
25397e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2540de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        return ContentUris.withAppendedId(uri, id);
2541a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2542a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2543a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2544e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * If account is non-null then store it in the values. If the account is
2545e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * already specified in the values then it must be consistent with the
2546e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * account, if it is non-null.
2547e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *
2548e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param uri Current {@link Uri} being operated on.
2549e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param values {@link ContentValues} to read and possibly update.
2550e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when only one of
2551e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_NAME} or
2552e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_TYPE} is specified, leaving the
2553e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             other undefined.
2554e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when {@link RawContacts#ACCOUNT_NAME}
2555e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             and {@link RawContacts#ACCOUNT_TYPE} are inconsistent between
2556e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             the given {@link Uri} and {@link ContentValues}.
25577e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
2558e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey    private Account resolveAccount(Uri uri, ContentValues values) throws IllegalArgumentException {
2559f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
2560f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
2561e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
2562f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2563f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
2564f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
2565e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialValues = TextUtils.isEmpty(valueAccountName)
2566e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                ^ TextUtils.isEmpty(valueAccountType);
2567e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2568e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri || partialValues) {
2569e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
2570fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
2571fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
2572e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
2573e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2574e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
2575e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
2576e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validUri = !TextUtils.isEmpty(accountName);
2577e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validValues = !TextUtils.isEmpty(valueAccountName);
2578e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2579e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validValues && validUri) {
2580e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Check that accounts match when both present
2581e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            final boolean accountMatch = TextUtils.equals(accountName, valueAccountName)
2582e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    && TextUtils.equals(accountType, valueAccountType);
2583e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            if (!accountMatch) {
2584fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new IllegalArgumentException(mDbHelper.exceptionMessage(
2585fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        "When both specified, ACCOUNT_NAME and ACCOUNT_TYPE must match", uri));
2586e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            }
2587e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validUri) {
2588e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Fill values from Uri when not present
2589f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_NAME, accountName);
2590f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_TYPE, accountType);
2591e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validValues) {
2592f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountName = valueAccountName;
2593f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountType = valueAccountType;
2594e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else {
2595e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            return null;
2596f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
2597f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2598e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Use cached Account object when matches, otherwise create
2599f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mAccount == null
2600f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.name.equals(accountName)
2601f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.type.equals(accountType)) {
2602f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mAccount = new Account(accountName, accountType);
2603035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
2604f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2605e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        return mAccount;
26067e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
26077e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
26087e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
2609d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Inserts an item in the contacts table
26106bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
26116bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
26126bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
26136bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
2614d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertContact(ContentValues values) {
2615de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregate contacts are created automatically");
26166bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
26176bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
26186bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
2619a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the contacts table
2620a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2621f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param uri the values for the new row
2622f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param values the account this contact should be associated with. may be null.
2623dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana     * @param callerIsSyncAdapter
2624a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2625a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2626dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private long insertRawContact(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
2627f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2628f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2629f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2630f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2631e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final Account account = resolveAccount(uri, mValues);
26327e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
26333d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
26343d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
2635f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
26363d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
26373d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2638f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long rawContactId = mDb.insert(Tables.RAW_CONTACTS, RawContacts.CONTACT_ID, mValues);
2639f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        int aggregationMode = RawContacts.AGGREGATION_MODE_DEFAULT;
2640f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        if (mValues.containsKey(RawContacts.AGGREGATION_MODE)) {
2641f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            aggregationMode = mValues.getAsInteger(RawContacts.AGGREGATION_MODE);
2642f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        }
2643f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        mContactAggregator.markNewForAggregation(rawContactId, aggregationMode);
2644285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2645285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        // Trigger creation of a Contact based on this RawContact at the end of transaction
2646e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        mInsertedRawContacts.put(rawContactId, account);
2647f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2648dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter) {
2649dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            addAutoAddMembership(rawContactId);
2650dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            final Long starred = values.getAsLong(RawContacts.STARRED);
2651dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (starred != null && starred != 0) {
2652dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateFavoritesMembership(rawContactId, starred != 0);
2653dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2654dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2655dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
26563826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
2657023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        return rawContactId;
2658a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2659a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2660dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void addAutoAddMembership(long rawContactId) {
2661dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID,
2662dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2663dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2664dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            insertDataGroupMembership(rawContactId, groupId);
2665dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2666dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2667dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2668dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private Long findGroupByRawContactId(String selection, long rawContactId) {
2669dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        Cursor c = mDb.query(Tables.GROUPS + "," + Tables.RAW_CONTACTS, PROJECTION_GROUP_ID,
2670dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection,
2671dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                new String[]{Long.toString(rawContactId)},
2672dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                null /* groupBy */, null /* having */, null /* orderBy */);
2673dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        try {
2674dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            while (c.moveToNext()) {
2675dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return c.getLong(0);
2676dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2677dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return null;
2678dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        } finally {
2679dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            c.close();
2680dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2681dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2682dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2683dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void updateFavoritesMembership(long rawContactId, boolean isStarred) {
2684dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID,
2685dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2686dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2687dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (isStarred) {
2688dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                insertDataGroupMembership(rawContactId, groupId);
2689dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
2690dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                deleteDataGroupMembership(rawContactId, groupId);
2691dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2692dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2693dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2694dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2695dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void insertDataGroupMembership(long rawContactId, long groupId) {
2696dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        ContentValues groupMembershipValues = new ContentValues();
2697dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.GROUP_ROW_ID, groupId);
2698dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.RAW_CONTACT_ID, rawContactId);
2699dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(DataColumns.MIMETYPE_ID,
2700dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                mDbHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
2701dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mDb.insert(Tables.DATA, null, groupMembershipValues);
2702dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2703dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2704dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void deleteDataGroupMembership(long rawContactId, long groupId) {
2705dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final String[] selectionArgs = {
2706dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(mDbHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE)),
2707dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(groupId),
2708dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(rawContactId)};
2709dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mDb.delete(Tables.DATA, SELECTION_GROUPMEMBERSHIP_DATA, selectionArgs);
2710dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2711dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2712a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2713a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
2714a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2715a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
2716a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2717a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2718f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
2719a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
2720de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.clear();
2721de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.putAll(values);
272267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
2723de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
272420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2725de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace package with internal mapping
2726de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
2727de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (packageName != null) {
2728b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
2729de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
2730de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.RES_PACKAGE);
2731508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2732de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace mimetype with internal mapping
2733de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String mimeType = mValues.getAsString(Data.MIMETYPE);
2734de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (TextUtils.isEmpty(mimeType)) {
2735de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
2736de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
27374097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
2738b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.getMimeTypeId(mimeType));
2739de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
2740a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2741a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
2742a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        id = rowHandler.insert(mDb, rawContactId, mValues);
2743f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2744de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            setRawContactDirty(rawContactId);
2745a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2746b5a4add17815167d20a90645779df34cdf45280dFred Quintana        mUpdatedRawContacts.add(rawContactId);
2747a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
27484f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
27494f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
27508e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov    private void triggerAggregation(long rawContactId) {
27518e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
27528e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            return;
27538e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        }
27548e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
2755b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        int aggregationMode = mDbHelper.getAggregationMode(rawContactId);
2756f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        switch (aggregationMode) {
27578e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_DISABLED:
27588e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                break;
27598e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
27608e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_DEFAULT: {
276169cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                mContactAggregator.markForAggregation(rawContactId, aggregationMode, false);
2762f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
27638e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
27648e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
27658e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_SUSPENDED: {
2766b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                long contactId = mDbHelper.getContactId(rawContactId);
2767f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
27688e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                if (contactId != 0) {
27698e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                    mContactAggregator.updateAggregateData(contactId);
27708e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                }
2771f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
27728e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
2773f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
2774c100221f706afc08409e8317a27d6850b11c54d3Omari Stephens            case RawContacts.AGGREGATION_MODE_IMMEDIATE: {
27750dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                mContactAggregator.aggregateContact(mDb, rawContactId);
2776f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
27778e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
2778f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        }
2779f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
2780f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
2781a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
27825ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov     * Returns the group id of the group with sourceId and the same account as rawContactId.
27839261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * If the group doesn't already exist then it is first created,
27849261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param db SQLiteDatabase to use for this operation
27855ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov     * @param rawContactId the contact this group is associated with
27869261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param sourceId the sourceIf of the group to query or create
27879261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @return the group id of the existing or created group
27889261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalArgumentException if the contact is not associated with an account
27899261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalStateException if a group needs to be created but the creation failed
27909261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     */
2791ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    private long getOrMakeGroup(SQLiteDatabase db, long rawContactId, String sourceId,
2792ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            Account account) {
2793ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2794ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        if (account == null) {
27954da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(rawContactId);
2796ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            Cursor c = db.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS,
27974da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    RawContacts._ID + "=?", mSelectionArgs1, null, null, null);
2798ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            try {
2799ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                if (c.moveToFirst()) {
2800ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    String accountName = c.getString(RawContactsQuery.ACCOUNT_NAME);
2801ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    String accountType = c.getString(RawContactsQuery.ACCOUNT_TYPE);
2802ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
2803ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        account = new Account(accountName, accountType);
2804ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    }
28059261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
2806ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            } finally {
2807ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                c.close();
28089261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
28099261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
2810ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
28119261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        if (account == null) {
28129261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            throw new IllegalArgumentException("if the groupmembership only "
2813ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    + "has a sourceid the the contact must be associated with "
28149261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    + "an account");
28159261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
28169261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
2817ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        ArrayList<GroupIdCacheEntry> entries = mGroupIdCache.get(sourceId);
2818ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        if (entries == null) {
2819ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            entries = new ArrayList<GroupIdCacheEntry>(1);
2820ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            mGroupIdCache.put(sourceId, entries);
2821ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        }
2822ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2823ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int count = entries.size();
2824ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        for (int i = 0; i < count; i++) {
2825ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            GroupIdCacheEntry entry = entries.get(i);
2826ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            if (entry.accountName.equals(account.name) && entry.accountType.equals(account.type)) {
2827ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                return entry.groupId;
2828ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            }
2829ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        }
2830ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2831ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        GroupIdCacheEntry entry = new GroupIdCacheEntry();
2832ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entry.accountName = account.name;
2833ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entry.accountType = account.type;
2834ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entry.sourceId = sourceId;
2835ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entries.add(0, entry);
2836ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
28379261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // look up the group that contains this sourceId and has the same account name and type
28385ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        // as the contact refered to by rawContactId
2839ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        Cursor c = db.query(Tables.GROUPS, new String[]{RawContacts._ID},
28409261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Clauses.GROUP_HAS_ACCOUNT_AND_SOURCE_ID,
2841df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                new String[]{sourceId, account.name, account.type}, null, null, null);
28429261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        try {
2843ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            if (c.moveToFirst()) {
2844ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                entry.groupId = c.getLong(0);
28459261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            } else {
28469261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                ContentValues groupValues = new ContentValues();
2847df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                groupValues.put(Groups.ACCOUNT_NAME, account.name);
2848df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                groupValues.put(Groups.ACCOUNT_TYPE, account.type);
28499261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                groupValues.put(Groups.SOURCE_ID, sourceId);
28509261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                long groupId = db.insert(Tables.GROUPS, Groups.ACCOUNT_NAME, groupValues);
28519261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (groupId < 0) {
28529261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    throw new IllegalStateException("unable to create a new group with "
28539261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            + "this sourceid: " + groupValues);
28549261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
2855ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                entry.groupId = groupId;
28569261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
28579261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        } finally {
28589261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            c.close();
28599261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
2860ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2861ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        return entry.groupId;
28629261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    }
28639261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
2864d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    private interface DisplayNameQuery {
28651129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        public static final String RAW_SQL =
28661129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                "SELECT "
28671129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        + DataColumns.MIMETYPE_ID + ","
28681129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        + Data.IS_PRIMARY + ","
28691129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        + Data.DATA1 + ","
28705dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA2 + ","
28715dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA3 + ","
28725dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA4 + ","
28735dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA5 + ","
28745dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA6 + ","
28755dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA7 + ","
28765dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA8 + ","
28775dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA9 + ","
28785dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA10 + ","
28795dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA11 +
28801129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                " FROM " + Tables.DATA +
28811129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                " WHERE " + Data.RAW_CONTACT_ID + "=?" +
28821129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        " AND (" + Data.DATA1 + " NOT NULL OR " +
28831129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                                Organization.TITLE + " NOT NULL)";
2884d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
2885d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        public static final int MIMETYPE = 0;
2886d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        public static final int IS_PRIMARY = 1;
28875dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int DATA1 = 2;
28885dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int GIVEN_NAME = 3;                         // data2
28895dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int FAMILY_NAME = 4;                        // data3
28905dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int PREFIX = 5;                             // data4
28915dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int TITLE = 5;                              // data4
28925dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int MIDDLE_NAME = 6;                        // data5
28935dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int SUFFIX = 7;                             // data6
28945dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int PHONETIC_GIVEN_NAME = 8;                // data7
28955dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int PHONETIC_MIDDLE_NAME = 9;               // data8
28965dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int ORGANIZATION_PHONETIC_NAME = 9;         // data8
28975dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int PHONETIC_FAMILY_NAME = 10;              // data9
28985dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int FULL_NAME_STYLE = 11;                   // data10
28995dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int ORGANIZATION_PHONETIC_NAME_STYLE = 11;  // data10
29005dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int PHONETIC_NAME_STYLE = 12;               // data11
2901d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    }
2902d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
2903d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    /**
2904d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov     * Updates a raw contact display name based on data rows, e.g. structured name,
2905d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov     * organization, email etc.
2906d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov     */
2907ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
2908bca1c8b44f99528fc123d5547723e44771e8e934Mike Lockwood        int bestDisplayNameSource = DisplayNameSources.UNDEFINED;
29095dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        NameSplitter.Name bestName = null;
29105dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String bestDisplayName = null;
29115dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String bestPhoneticName = null;
29125dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        int bestPhoneticNameStyle = PhoneticNameStyle.UNDEFINED;
2913d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
29141129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(rawContactId);
29151129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        Cursor c = db.rawQuery(DisplayNameQuery.RAW_SQL, mSelectionArgs1);
2916d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        try {
2917d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            while (c.moveToNext()) {
29181129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                int mimeType = c.getInt(DisplayNameQuery.MIMETYPE);
29195dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                int source = getDisplayNameSource(mimeType);
29205dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                if (source < bestDisplayNameSource || source == DisplayNameSources.UNDEFINED) {
29215dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    continue;
29225dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                }
29231129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
29245dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                if (source == bestDisplayNameSource && c.getInt(DisplayNameQuery.IS_PRIMARY) == 0) {
29255dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    continue;
2926d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                }
29271129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
29285dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                if (mimeType == mMimeTypeIdStructuredName) {
29295dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    NameSplitter.Name name;
29305dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    if (bestName != null) {
29315dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        name = new NameSplitter.Name();
29325dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    } else {
29335dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        name = mName;
29345dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        name.clear();
29355dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    }
29365dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.prefix = c.getString(DisplayNameQuery.PREFIX);
29375dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.givenNames = c.getString(DisplayNameQuery.GIVEN_NAME);
29385dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.middleName = c.getString(DisplayNameQuery.MIDDLE_NAME);
29395dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.familyName = c.getString(DisplayNameQuery.FAMILY_NAME);
29405dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.suffix = c.getString(DisplayNameQuery.SUFFIX);
29415dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.fullNameStyle = c.isNull(DisplayNameQuery.FULL_NAME_STYLE)
29425dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            ? FullNameStyle.UNDEFINED
29435dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            : c.getInt(DisplayNameQuery.FULL_NAME_STYLE);
29445dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.phoneticFamilyName = c.getString(DisplayNameQuery.PHONETIC_FAMILY_NAME);
29455dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.phoneticMiddleName = c.getString(DisplayNameQuery.PHONETIC_MIDDLE_NAME);
29465dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.phoneticGivenName = c.getString(DisplayNameQuery.PHONETIC_GIVEN_NAME);
29475dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.phoneticNameStyle = c.isNull(DisplayNameQuery.PHONETIC_NAME_STYLE)
29485dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            ? PhoneticNameStyle.UNDEFINED
29495dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            : c.getInt(DisplayNameQuery.PHONETIC_NAME_STYLE);
29505dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    if (!name.isEmpty()) {
29515dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestDisplayNameSource = source;
29525dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestName = name;
29535dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    }
29545dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                } else if (mimeType == mMimeTypeIdOrganization) {
29555dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    mCharArrayBuffer.sizeCopied = 0;
29565dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    c.copyStringToBuffer(DisplayNameQuery.DATA1, mCharArrayBuffer);
29575dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    if (mCharArrayBuffer.sizeCopied != 0) {
2958d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                        bestDisplayNameSource = source;
29591129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        bestDisplayName = new String(mCharArrayBuffer.data, 0,
29601129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                                mCharArrayBuffer.sizeCopied);
29615dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestPhoneticName = c.getString(DisplayNameQuery.ORGANIZATION_PHONETIC_NAME);
29625dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestPhoneticNameStyle =
29635dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                                c.isNull(DisplayNameQuery.ORGANIZATION_PHONETIC_NAME_STYLE)
29645dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                                    ? PhoneticNameStyle.UNDEFINED
29655dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                                    : c.getInt(DisplayNameQuery.ORGANIZATION_PHONETIC_NAME_STYLE);
29665dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    } else {
29675dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        c.copyStringToBuffer(DisplayNameQuery.TITLE, mCharArrayBuffer);
29685dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        if (mCharArrayBuffer.sizeCopied != 0) {
29691129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                            bestDisplayNameSource = source;
29701129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                            bestDisplayName = new String(mCharArrayBuffer.data, 0,
29711129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                                    mCharArrayBuffer.sizeCopied);
29725dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            bestPhoneticName = null;
29735dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            bestPhoneticNameStyle = PhoneticNameStyle.UNDEFINED;
29741129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        }
2975d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                    }
29765dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                } else {
29775dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    // Display name is at DATA1 in all other types.
29785dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    // This is ensured in the constructor.
29795dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
29805dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    mCharArrayBuffer.sizeCopied = 0;
29815dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    c.copyStringToBuffer(DisplayNameQuery.DATA1, mCharArrayBuffer);
29825dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    if (mCharArrayBuffer.sizeCopied != 0) {
29835dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestDisplayNameSource = source;
29845dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestDisplayName = new String(mCharArrayBuffer.data, 0,
29855dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                                mCharArrayBuffer.sizeCopied);
29865dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestPhoneticName = null;
29875dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestPhoneticNameStyle = PhoneticNameStyle.UNDEFINED;
29885dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    }
2989d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                }
2990d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            }
2991d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
2992d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        } finally {
2993d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            c.close();
2994d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        }
2995d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
29965dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String displayNamePrimary;
29975dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String displayNameAlternative;
29985dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String sortKeyPrimary = null;
29995dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String sortKeyAlternative = null;
30005dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        int displayNameStyle = FullNameStyle.UNDEFINED;
30015dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
30025dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        if (bestDisplayNameSource == DisplayNameSources.STRUCTURED_NAME) {
30035dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            displayNameStyle = bestName.fullNameStyle;
30045dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            if (displayNameStyle == FullNameStyle.CJK
30055dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    || displayNameStyle == FullNameStyle.UNDEFINED) {
30065dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                displayNameStyle = mNameSplitter.getAdjustedFullNameStyle(displayNameStyle);
30075dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bestName.fullNameStyle = displayNameStyle;
30085dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            }
30095dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
30105dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            displayNamePrimary = mNameSplitter.join(bestName, true);
30115dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            displayNameAlternative = mNameSplitter.join(bestName, false);
30125dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
30135dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            bestPhoneticName = mNameSplitter.joinPhoneticName(bestName);
30145dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            bestPhoneticNameStyle = bestName.phoneticNameStyle;
30155dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        } else {
30165dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            displayNamePrimary = displayNameAlternative = bestDisplayName;
30175dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        }
30185dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
30195dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        if (bestPhoneticName != null) {
30205dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            sortKeyPrimary = sortKeyAlternative = bestPhoneticName;
30215dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            if (bestPhoneticNameStyle == PhoneticNameStyle.UNDEFINED) {
30225dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bestPhoneticNameStyle = mNameSplitter.guessPhoneticNameStyle(bestPhoneticName);
30235dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            }
30245dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        } else {
30255dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            if (displayNameStyle == FullNameStyle.UNDEFINED) {
30265dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                displayNameStyle = mNameSplitter.guessFullNameStyle(bestDisplayName);
30275dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                if (displayNameStyle == FullNameStyle.UNDEFINED
30285dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        || displayNameStyle == FullNameStyle.CJK) {
30295dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    displayNameStyle = mNameSplitter.getAdjustedNameStyleBasedOnPhoneticNameStyle(
30305dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            displayNameStyle, bestPhoneticNameStyle);
30315dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                }
30325dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                displayNameStyle = mNameSplitter.getAdjustedFullNameStyle(displayNameStyle);
30335dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            }
3034ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao            if (displayNameStyle == FullNameStyle.CHINESE ||
3035ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao                    displayNameStyle == FullNameStyle.CJK) {
30365dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                sortKeyPrimary = sortKeyAlternative =
3037cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao                        ContactLocaleUtils.getIntance().getSortKey(
3038ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao                                displayNamePrimary, displayNameStyle);
30395dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            }
30405dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        }
30415dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
30425dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        if (sortKeyPrimary == null) {
30435dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            sortKeyPrimary = displayNamePrimary;
30445dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            sortKeyAlternative = displayNameAlternative;
30455dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        }
30465dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
30475dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        setDisplayName(rawContactId, bestDisplayNameSource, displayNamePrimary,
30485dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                displayNameAlternative, bestPhoneticName, bestPhoneticNameStyle,
30495dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                sortKeyPrimary, sortKeyAlternative);
3050d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    }
3051d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
30521129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private int getDisplayNameSource(int mimeTypeId) {
30531129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        if (mimeTypeId == mMimeTypeIdStructuredName) {
30541129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.STRUCTURED_NAME;
30551129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdEmail) {
30561129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.EMAIL;
30571129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdPhone) {
30581129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.PHONE;
30591129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdOrganization) {
30601129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.ORGANIZATION;
30611129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdNickname) {
30621129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.NICKNAME;
30631129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else {
30641129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.UNDEFINED;
30651129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        }
30661129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    }
30671129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
30689261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
306920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
307020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
3071f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
307220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
307320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3074de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
3075de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
307614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataDeleteQuery.COLUMNS, selection, selectionArgs, null);
3077de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        try {
3078de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            while(c.moveToNext()) {
307914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
308014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                String mimeType = c.getString(DataDeleteQuery.MIMETYPE);
3081a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                DataRowHandler rowHandler = getDataRowHandler(mimeType);
3082a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                count += rowHandler.delete(mDb, c);
3083f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                if (!callerIsSyncAdapter) {
308488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                    setRawContactDirty(rawContactId);
308588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                }
308620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
308720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
3088de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            c.close();
308920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
309020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
309120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
309220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
309320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
309488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    /**
309588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     * Delete a data row provided that it is one of the allowed mime types.
309688e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     */
309720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
3098f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
309988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
310088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
31014da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(dataId);
31024da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataDeleteQuery.COLUMNS, Data._ID + "=?",
31034da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1, null);
3104f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
310520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
310620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
310720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
310820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
310920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
311014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String mimeType = c.getString(DataDeleteQuery.MIMETYPE);
311120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
311220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
311320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
311420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
311520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
311620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
311720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
311820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
311920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
31207a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException("Data type mismatch: expected "
312120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
312220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
312320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3124a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            DataRowHandler rowHandler = getDataRowHandler(mimeType);
3125813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return rowHandler.delete(mDb, c);
312620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
312720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
312820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
312920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
313020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
313120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
3132ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
3133ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
3134f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private long insertGroup(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
3135f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
3136f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
3137f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
3138e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final Account account = resolveAccount(uri, mValues);
3139ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3140ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
3141f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String packageName = mValues.getAsString(Groups.RES_PACKAGE);
314267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
3143f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(GroupsColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
314467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
3145f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.remove(Groups.RES_PACKAGE);
3146ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3147dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final boolean isFavoritesGroup = mValues.getAsLong(Groups.FAVORITES) != null
3148dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                ? mValues.getAsLong(Groups.FAVORITES) != 0
3149dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                : false;
3150dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3151f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
3152f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(Groups.DIRTY, 1);
315373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
315473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3155f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long result = mDb.insert(Tables.GROUPS, Groups.TITLE, mValues);
3156ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
3157dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter && isFavoritesGroup) {
3158dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // add all starred raw contacts to this group
3159dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String selection;
3160dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs;
3161dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (account == null) {
3162dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + " IS NULL AND "
3163dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + RawContacts.ACCOUNT_TYPE + " IS NULL";
3164dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = null;
3165dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
3166dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + "=? AND "
3167dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + RawContacts.ACCOUNT_TYPE + "=?";
3168dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = new String[]{account.name, account.type};
3169dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
3170dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            Cursor c = mDb.query(Tables.RAW_CONTACTS,
3171dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[]{RawContacts._ID, RawContacts.STARRED},
3172dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    selection, selectionArgs, null, null, null);
3173892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            try {
3174892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                while (c.moveToNext()) {
3175892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (c.getLong(1) != 0) {
3176892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        final long rawContactId = c.getLong(0);
3177892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        insertDataGroupMembership(rawContactId, result);
3178892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        setRawContactDirty(rawContactId);
3179892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    }
3180dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
3181892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            } finally {
3182892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                c.close();
3183dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
3184dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
3185dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3186f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mValues.containsKey(Groups.GROUP_VISIBLE)) {
31871a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3188ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        }
3189ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
3190ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        return result;
3191ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
3192ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
31935aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private long insertSettings(Uri uri, ContentValues values) {
3194e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final long id = mDb.insert(Tables.SETTINGS, null, values);
31955aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
31961a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
31971a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3198e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
31991a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
3200e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return id;
3201e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3202e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3203ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
320482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov     * Inserts a status update.
32051f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
320682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    public long insertStatusUpdate(ContentValues values) {
320782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        final String handle = values.getAsString(StatusUpdates.IM_HANDLE);
32080a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        final Integer protocol = values.getAsInteger(StatusUpdates.PROTOCOL);
32094dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        String customProtocol = null;
32104dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
32110a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        if (protocol != null && protocol == Im.PROTOCOL_CUSTOM) {
321282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            customProtocol = values.getAsString(StatusUpdates.CUSTOM_PROTOCOL);
32134dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (TextUtils.isEmpty(customProtocol)) {
32144dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                throw new IllegalArgumentException(
32154dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                        "CUSTOM_PROTOCOL is required when PROTOCOL=PROTOCOL_CUSTOM");
32164dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
32171f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
32181f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3219dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long rawContactId = -1;
3220dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long contactId = -1;
322182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Long dataId = values.getAsLong(StatusUpdates.DATA_ID);
3222f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.setLength(0);
32232526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov        mSelectionArgs.clear();
3224dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        if (dataId != null) {
3225dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the contact info for the given data row.
3226dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
32272526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSb.append(Tables.DATA + "." + Data._ID + "=?");
32282526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSelectionArgs.add(String.valueOf(dataId));
32291f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
3230dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the data row to attach this presence update to
3231dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
32320a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(handle) || protocol == null) {
32330a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                throw new IllegalArgumentException("PROTOCOL and IM_HANDLE are required");
32340a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
32350a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
3236dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // TODO: generalize to allow other providers to match against email
3237dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == protocol;
3238dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
32392526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            String mimeTypeIdIm = String.valueOf(mMimeTypeIdIm);
3240dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            if (matchEmail) {
32412526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                String mimeTypeIdEmail = String.valueOf(mMimeTypeIdEmail);
3242f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
3243f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // The following hack forces SQLite to use the (mimetype_id,data1) index, otherwise
3244f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the "OR" conjunction confuses it and it switches to a full scan of
3245f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the raw_contacts table.
3246f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
3247f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // This code relies on the fact that Im.DATA and Email.DATA are in fact the same
3248f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // column - Data.DATA1
32492526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + " IN (?,?)" +
32502526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Data.DATA1 + "=?" +
32512526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND ((" + DataColumns.MIMETYPE_ID + "=? AND " + Im.PROTOCOL + "=?");
32522526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
32532526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
32542526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
32552526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
32562526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
3257dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
32582526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
32592526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
3260dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
32612526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(") OR (" + DataColumns.MIMETYPE_ID + "=?))");
32622526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
3263dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            } else {
32642526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + "=?" +
32652526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.PROTOCOL + "=?" +
32662526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.DATA + "=?");
32672526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
32682526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
32692526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
3270dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
32712526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
32722526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
3273dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
3274dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
32751f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
327682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.DATA_ID)) {
32772526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(" AND " + DataColumns.CONCRETE_ID + "=?");
32782526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(values.getAsString(StatusUpdates.DATA_ID));
3279dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
328070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
3281f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.append(" AND ").append(getContactsRestrictions());
328270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
32831f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
32841f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
3285de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            cursor = mDb.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
32862526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.toString(), mSelectionArgs.toArray(EMPTY_STRING_ARRAY), null, null,
32874394086494fe7909aaca70f56fb4bb08beebf303Dmitri Plotnikov                    Clauses.CONTACT_VISIBLE + " DESC, " + Data.RAW_CONTACT_ID);
32881f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
328967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
32905ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
3291e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
32921f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
32931f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
32941f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
32951f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
32961f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
329731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
329831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
329931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
33001f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
33011f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
330282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.PRESENCE)) {
3303a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (customProtocol == null) {
3304a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // We cannot allow a null in the custom protocol field, because SQLite3 does not
3305a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // properly enforce uniqueness of null values
3306a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                customProtocol = "";
3307a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
3308a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3309a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.clear();
331082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.DATA_ID, dataId);
3311a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.RAW_CONTACT_ID, rawContactId);
3312a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.CONTACT_ID, contactId);
331382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PROTOCOL, protocol);
331482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
331582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.IM_HANDLE, handle);
331682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.IM_ACCOUNT)) {
331782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                mValues.put(StatusUpdates.IM_ACCOUNT, values.getAsString(StatusUpdates.IM_ACCOUNT));
3318a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
331982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PRESENCE,
332082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    values.getAsString(StatusUpdates.PRESENCE));
3321aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori            mValues.put(StatusUpdates.CHAT_CAPABILITY,
3322aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                    values.getAsString(StatusUpdates.CHAT_CAPABILITY));
33231f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3324a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            // Insert the presence update
3325a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mDb.replace(Tables.PRESENCE, null, mValues);
3326a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3327e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
33280a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
332982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.STATUS)) {
333082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String status = values.getAsString(StatusUpdates.STATUS);
33310a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String resPackage = values.getAsString(StatusUpdates.STATUS_RES_PACKAGE);
33320a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Integer labelResource = values.getAsInteger(StatusUpdates.STATUS_LABEL);
33330a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
33340a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(resPackage)
33350a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && (labelResource == null || labelResource == 0)
33360a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && protocol != null) {
33370a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                labelResource = Im.getProtocolLabelResource(protocol);
33380a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
33390a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
33400a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Long iconResource = values.getAsLong(StatusUpdates.STATUS_ICON);
33410a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            // TODO compute the default icon based on the protocol
33420a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
3343a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (TextUtils.isEmpty(status)) {
3344a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateDelete.bindLong(1, dataId);
3345a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateDelete.execute();
334682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            } else if (values.containsKey(StatusUpdates.STATUS_TIMESTAMP)) {
334782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                long timestamp = values.getAsLong(StatusUpdates.STATUS_TIMESTAMP);
3348a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.bindLong(1, dataId);
3349a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.bindLong(2, timestamp);
33505dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bindString(mStatusUpdateReplace, 3, status);
33515dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bindString(mStatusUpdateReplace, 4, resPackage);
33525dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bindLong(mStatusUpdateReplace, 5, iconResource);
33535dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bindLong(mStatusUpdateReplace, 6, labelResource);
3354a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.execute();
3355a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            } else {
3356a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3357a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                try {
3358a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateInsert.bindLong(1, dataId);
33595dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindString(mStatusUpdateInsert, 2, status);
33605dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindString(mStatusUpdateInsert, 3, resPackage);
33615dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindLong(mStatusUpdateInsert, 4, iconResource);
33625dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindLong(mStatusUpdateInsert, 5, labelResource);
3363a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateInsert.executeInsert();
3364a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                } catch (SQLiteConstraintException e) {
3365a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    // The row already exists - update it
33660a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    long timestamp = System.currentTimeMillis();
3367a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.bindLong(1, timestamp);
33685dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindString(mStatusUpdateAutoTimestamp, 2, status);
3369a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.bindLong(3, dataId);
33705dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindString(mStatusUpdateAutoTimestamp, 4, status);
3371a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.execute();
33720a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
33735dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindString(mStatusAttributionUpdate, 1, resPackage);
33745dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindLong(mStatusAttributionUpdate, 2, iconResource);
33755dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindLong(mStatusAttributionUpdate, 3, labelResource);
33760a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    mStatusAttributionUpdate.bindLong(4, dataId);
33770a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    mStatusAttributionUpdate.execute();
3378a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                }
3379e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            }
3380e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        }
3381bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
3382a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (contactId != -1) {
3383f4015ab9ab7c26b766b5331fbf6655b8c54877eaDmitri Plotnikov            mContactAggregator.updateLastStatusUpdateId(contactId);
3384a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3385a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3386a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        return dataId;
33871f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
33881f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
33894f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3390de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
3391bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3392b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "deleteInTransaction: " + uri);
3393b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3394b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3395f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3396f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
3397508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
3398508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
339935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3400b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs);
340135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3402b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID:
3403b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3404b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3405b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
3406b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selectionWithId, selectionArgs);
3407b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3408cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            case CONTACTS: {
3409cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // TODO
3410cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return 0;
3411cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3412cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3413d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
3414d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3415dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
34166bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
34176bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
34189fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP: {
34192e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
34202e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
34212e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
3422fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
3423fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
34242e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
34252e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
34262e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
3427dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
34282e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
34292e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
34309fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP_ID: {
34319fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                // lookup contact by id and lookup key to see if they still match the actual record
34329fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final List<String> pathSegments = uri.getPathSegments();
34339fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final String lookupKey = pathSegments.get(2);
34349fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
34359fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                setTablesAndProjectionMapForContacts(lookupQb, uri, null);
3436a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
34379fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                String[] args;
34389fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                if (selectionArgs == null) {
34399fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[2];
34409fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } else {
34419fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[selectionArgs.length + 2];
34429fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
34439fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
34449fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                args[0] = String.valueOf(contactId);
344560de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann                args[1] = Uri.encode(lookupKey);
34469fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                lookupQb.appendWhere(Contacts._ID + "=? AND " + Contacts.LOOKUP_KEY + "=?");
34479fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final SQLiteDatabase db = mDbHelper.getReadableDatabase();
34489fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                Cursor c = query(db, lookupQb, null, selection, args, null, null, null);
34499fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                try {
34509fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    if (c.getCount() == 1) {
34519fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // contact was unmodified so go ahead and delete it
3452dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        return deleteContact(contactId, callerIsSyncAdapter);
34539fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    } else {
34549fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // row was changed (e.g. the merging might have changed), we got multiple
34559fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // rows or the supplied selection filtered the record out
34569fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        return 0;
34579fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    }
34589fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } finally {
34599fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    c.close();
34609fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
34619fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            }
34629fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
34632971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case RAW_CONTACTS: {
34642971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
3465fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                Cursor c = mDb.query(Tables.RAW_CONTACTS,
3466fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        new String[]{RawContacts._ID, RawContacts.CONTACT_ID},
3467e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
34682971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
34692971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
34702971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                        final long rawContactId = c.getLong(0);
3471fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        long contactId = c.getLong(1);
3472fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        numDeletes += deleteRawContact(rawContactId, contactId,
3473fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                                callerIsSyncAdapter);
34742971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
34752971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
34762971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
34772971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
34782971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
34792971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
34802971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
34815ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
34822971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                final long rawContactId = ContentUris.parseId(uri);
3483fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                return deleteRawContact(rawContactId, mDbHelper.getContactId(rawContactId),
3484fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        callerIsSyncAdapter);
3485508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3486508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
348720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
3488f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
3489944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                return deleteData(appendAccountToSelection(uri, selection), selectionArgs,
3490f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        callerIsSyncAdapter);
349120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
349220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
349348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
349448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
349548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
349648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
3497508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
3498f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
34994da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
35004da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                return deleteData(Data._ID + "=?", mSelectionArgs1, callerIsSyncAdapter);
3501ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3502ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3503ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3504f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
35055aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteGroup(uri, ContentUris.parseId(uri), callerIsSyncAdapter);
35062971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
35072971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
35082971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case GROUPS: {
35092971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
35102971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups._ID},
3511e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
35122971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
35132971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
35145aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        numDeletes += deleteGroup(uri, c.getLong(0), callerIsSyncAdapter);
35152971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
35162971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
35172971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
35182971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
351981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (numDeletes > 0) {
3520f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
352181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
35222971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
3523508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3524508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
3525eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
352643880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3527e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                return deleteSettings(uri, appendAccountToSelection(uri, selection), selectionArgs);
3528eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3529eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
353082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
35310a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                return deleteStatusUpdates(selection, selectionArgs);
35321f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
35331f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
353481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
353581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
35363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
353781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
3538508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
35394f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
35404f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
35411c8e40c18f92722b9bec6e8ce2e345a9828efa16Dmitri Plotnikov    public int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) {
3542ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
3543b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final long groupMembershipMimetypeId = mDbHelper
354494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
3545de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mDb.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
354694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
354794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupId, null);
354894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
354994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        try {
3550f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (callerIsSyncAdapter) {
3551de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
355294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            } else {
355394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.clear();
355494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.put(Groups.DELETED, 1);
3555f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mValues.put(Groups.DIRTY, 1);
3556de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId, null);
355794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            }
355894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        } finally {
35591a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
356094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
356194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
356294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
35635aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
3564e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.delete(Tables.SETTINGS, selection, selectionArgs);
35651a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        mVisibleTouched = true;
3566e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3567e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3568e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3569dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int deleteContact(long contactId, boolean callerIsSyncAdapter) {
357096b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(contactId);
3571cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
357296b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                RawContacts.CONTACT_ID + "=?", mSelectionArgs1,
357396b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                null, null, null);
3574cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        try {
3575cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            while (c.moveToNext()) {
3576cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                long rawContactId = c.getLong(0);
3577dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
3578cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3579cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } finally {
3580cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            c.close();
3581cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
3582cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
35833826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
35843826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
3585cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return mDb.delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
3586cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3587cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3588fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov    public int deleteRawContact(long rawContactId, long contactId, boolean callerIsSyncAdapter) {
35893389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
35903826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
35913826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
3592f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (callerIsSyncAdapter) {
359314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            mDb.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
3594fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            int count = mDb.delete(Tables.RAW_CONTACTS, RawContacts._ID + "=" + rawContactId, null);
3595fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            mContactAggregator.updateDisplayNameForContact(mDb, contactId);
3596fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return count;
359733b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } else {
3598b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.removeContactIfSingleton(rawContactId);
3599dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
360033b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        }
360133b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
360233b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
36030a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private int deleteStatusUpdates(String selection, String[] selectionArgs) {
36049705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // delete from both tables: presence and status_updates
36059705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // TODO should account type/name be appended to the where clause?
36069705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      if (VERBOSE_LOGGING) {
36079705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          Log.v(TAG, "deleting data from status_updates for " + selection);
36089705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      }
36099705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      mDb.delete(Tables.STATUS_UPDATES, getWhereClauseForStatusUpdatesTable(selection),
36109705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          selectionArgs);
36119705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      return mDb.delete(Tables.PRESENCE, selection, selectionArgs);
36120a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
36130a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
3614dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int markRawContactAsDeleted(long rawContactId, boolean callerIsSyncAdapter) {
361581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = true;
361681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
3617cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.clear();
3618cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DELETED, 1);
3619cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
3620cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
3621cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
3622cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DIRTY, 1);
3623dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        return updateRawContact(rawContactId, mValues, callerIsSyncAdapter);
3624cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3625cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
36264f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3627de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
3628de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            String[] selectionArgs) {
3629bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3630b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "updateInTransaction: " + uri);
3631b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3632b5a4add17815167d20a90645779df34cdf45280dFred Quintana
363335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
363400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
363500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
3636b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (match == SYNCSTATE_ID && selection == null) {
3637b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long rowId = ContentUris.parseId(uri);
36381129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Object data = values.get(ContactsContract.SyncState.DATA);
3639b5a4add17815167d20a90645779df34cdf45280dFred Quintana            mUpdatedSyncStates.put(rowId, data);
3640b5a4add17815167d20a90645779df34cdf45280dFred Quintana            return 1;
3641b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3642b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3643f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3644f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
364500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
364635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3647b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
3648b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        appendAccountToSelection(uri, selection), selectionArgs);
3649b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3650b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID: {
3651b5a4add17815167d20a90645779df34cdf45280dFred Quintana                selection = appendAccountToSelection(uri, selection);
3652b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3653b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3654b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
3655b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
3656b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        selectionWithId, selectionArgs);
3657b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
365835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3659d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
3660dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(values, selection, selectionArgs, callerIsSyncAdapter);
366100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
366200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
366300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3664d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
3665dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(ContentUris.parseId(uri), values, callerIsSyncAdapter);
3666c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
3667c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
3668c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
36692e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
36702e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
36712e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
36722e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
36732e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
3674fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
3675fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
36762e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
36772e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
36782e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
3679dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(contactId, values, callerIsSyncAdapter);
36802e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                break;
36812e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
36822e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
36837d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            case RAW_CONTACTS_DATA: {
36847d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                final String rawContactId = uri.getPathSegments().get(1);
36857d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
36867d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                    + (selection == null ? "" : " AND " + selection);
36877d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
36887d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter);
36897d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
36907d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                break;
36917d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            }
36927d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
369320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
3694944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                count = updateData(uri, values, appendAccountToSelection(uri, selection),
3695f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
369681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3697f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
369881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
369920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
370020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3701c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
370248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
370348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
370448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
370548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
3706f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
370781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3708f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
370981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
371000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
371100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
37127e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
37135ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
37145ac5c70d1165309302ebcc931f51723e37d31e0bJeff Sharkey                selection = appendAccountToSelection(uri, selection);
3715dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateRawContacts(values, selection, selectionArgs, callerIsSyncAdapter);
37167e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
37177e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
37187e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
37195ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
372033b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
37214529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                if (selection != null) {
37224da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
37234da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=?"
3724dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                                    + " AND(" + selection + ")", selectionArgs,
3725dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
37264529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                } else {
37274da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(rawContactId);
3728dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    count = updateRawContacts(values, RawContacts._ID + "=?", mSelectionArgs1,
3729dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
37304529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                }
37317e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
37327e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
37337e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
3734ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
37355aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, appendAccountToSelection(uri, selection),
3736f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
373781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3738f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
373981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3740ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3741ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3742ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3743ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3744ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
37454da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(groupId));
37464da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                String selectionWithId = Groups._ID + "=? "
374773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                        + (selection == null ? "" : " AND " + selection);
37485aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, selectionWithId, selectionArgs,
37495aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        callerIsSyncAdapter);
375081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3751f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
375281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3753ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3754ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3755ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3756127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
3757de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                count = updateAggregationException(mDb, values);
3758b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
3759b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
3760b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
3761eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
3762e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                count = updateSettings(uri, values, appendAccountToSelection(uri, selection),
3763e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        selectionArgs);
376443880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3765eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
3766eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3767eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
37689705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            case STATUS_UPDATES: {
37699705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                count = updateStatusUpdate(uri, values, selection, selectionArgs);
37709705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                break;
37719705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            }
37729705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
377372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            case DIRECTORIES: {
377472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                mContactDirectoryManager.scheduleDirectoryUpdateForCaller();
377572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                count = 1;
3776d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
3777d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
3778d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
377981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
378081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
3781f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
378281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
378300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
378400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
378500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
37864f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
37874f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
37889705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private int updateStatusUpdate(Uri uri, ContentValues values, String selection,
37899705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        String[] selectionArgs) {
37909705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // update status_updates table, if status is provided
37919705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO should account type/name be appended to the where clause?
37929705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        int updateCount = 0;
37939705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContentValues settableValues = getSettableColumnsForStatusUpdatesTable(values);
37949705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
37959705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.STATUS_UPDATES,
37969705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    settableValues,
37979705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    getWhereClauseForStatusUpdatesTable(selection),
37989705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selectionArgs);
37999705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
38009705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
38019705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // now update the Presence table
38029705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        settableValues = getSettableColumnsForPresenceTable(values);
38039705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
38049705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.PRESENCE, settableValues,
38059705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selection, selectionArgs);
38069705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
38079705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO updateCount is not entirely a valid count of updated rows because 2 tables could
38089705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // potentially get updated in this method.
38099705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return updateCount;
38109705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
38119705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
38129705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    /**
38139705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     * Build a where clause to select the rows to be updated in status_updates table.
38149705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     */
38159705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private String getWhereClauseForStatusUpdatesTable(String selection) {
38169705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.setLength(0);
38179705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE);
38189705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(selection);
38199705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(")");
38209705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mSb.toString();
38219705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
38229705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
38239705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForStatusUpdatesTable(ContentValues values) {
38249705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
38259705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS, values,
38269705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS);
38279705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_TIMESTAMP, values,
38289705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_TIMESTAMP);
38299705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_RES_PACKAGE, values,
38309705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_RES_PACKAGE);
38319705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_LABEL, values,
38329705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_LABEL);
38339705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_ICON, values,
38349705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_ICON);
38359705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
38369705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
38379705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
38389705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForPresenceTable(ContentValues values) {
38399705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
38409705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.PRESENCE, values,
38419705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.PRESENCE);
3842aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.CHAT_CAPABILITY, values,
3843aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                StatusUpdates.CHAT_CAPABILITY);
38449705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
38459705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
38469705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
38475aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int updateGroups(Uri uri, ContentValues values, String selectionWithId,
3848f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
384973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3850ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
3851ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
385273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        ContentValues updatedValues;
3853f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter && !values.containsKey(Groups.DIRTY)) {
385473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = mValues;
385573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.clear();
385673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.putAll(values);
385773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.put(Groups.DIRTY, 1);
385873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } else {
385973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = values;
386073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
386173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3862ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        int count = mDb.update(Tables.GROUPS, updatedValues, selectionWithId, selectionArgs);
38631a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) {
38641a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
386594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
38666ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        if (updatedValues.containsKey(Groups.SHOULD_SYNC)
38671129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                && updatedValues.getAsInteger(Groups.SHOULD_SYNC) != 0) {
38686ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups.ACCOUNT_NAME,
3869e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    Groups.ACCOUNT_TYPE}, selectionWithId, selectionArgs, null,
38706ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    null, null);
38716ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountName;
38726ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountType;
38736ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            try {
38746ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                while (c.moveToNext()) {
38756ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountName = c.getString(0);
38766ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountType = c.getString(1);
38776ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    if(!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
38786ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        Account account = new Account(accountName, accountType);
3879ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        ContentResolver.requestSync(account, ContactsContract.AUTHORITY,
38806ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                                new Bundle());
38816ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        break;
38826ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    }
38836ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                }
38846ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            } finally {
38856ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                c.close();
38866ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            }
38876ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        }
388894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        return count;
388994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
389094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
3891b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private int updateSettings(Uri uri, ContentValues values, String selection,
3892b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            String[] selectionArgs) {
3893e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.update(Tables.SETTINGS, values, selection, selectionArgs);
38941a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
38951a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3896e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
3897e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3898e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3899e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3900dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContacts(ContentValues values, String selection, String[] selectionArgs,
3901dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
39024529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (values.containsKey(RawContacts.CONTACT_ID)) {
39034529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            throw new IllegalArgumentException(RawContacts.CONTACT_ID + " should not be included " +
39044529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    "in content values. Contact IDs are assigned automatically");
39054529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
390673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
390797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
390897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
390997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0");
391097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
391197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
39124529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = 0;
3913b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getRawContactView(),
391451bf5ea9531b9da72caff607dbdf35fd6f61cbe2Jeff Sharkey                new String[] { RawContacts._ID }, selection,
39154529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                selectionArgs, null, null, null);
39164529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        try {
39174529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            while (cursor.moveToNext()) {
39184529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                long rawContactId = cursor.getLong(0);
3919dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContact(rawContactId, values, callerIsSyncAdapter);
39204529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count++;
39214529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            }
39224529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        } finally {
39234529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            cursor.close();
39244529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
39254529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
39264529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return count;
39274529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    }
39284529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
3929dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContact(long rawContactId, ContentValues values,
3930dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
393196b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        final String selection = RawContacts._ID + " = ?";
393296b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(rawContactId);
393319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final boolean requestUndoDelete = (values.containsKey(RawContacts.DELETED)
393419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                && values.getAsInteger(RawContacts.DELETED) == 0);
393519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int previousDeleted = 0;
3936ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType = null;
3937ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName = null;
393819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        if (requestUndoDelete) {
393919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            Cursor cursor = mDb.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS, selection,
394096b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                    mSelectionArgs1, null, null, null);
394119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            try {
394219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                if (cursor.moveToFirst()) {
394319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    previousDeleted = cursor.getInt(RawContactsQuery.DELETED);
3944ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountType = cursor.getString(RawContactsQuery.ACCOUNT_TYPE);
3945ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountName = cursor.getString(RawContactsQuery.ACCOUNT_NAME);
394619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                }
394719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            } finally {
394819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                cursor.close();
394919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
395019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
395119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT);
395219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        }
3953f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
395496b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        int count = mDb.update(Tables.RAW_CONTACTS, values, selection, mSelectionArgs1);
39555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count != 0) {
3956f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            if (values.containsKey(RawContacts.AGGREGATION_MODE)) {
3957f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                int aggregationMode = values.getAsInteger(RawContacts.AGGREGATION_MODE);
3958f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov
3959f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // As per ContactsContract documentation, changing aggregation mode
3960f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // to DEFAULT should not trigger aggregation
3961f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                if (aggregationMode != RawContacts.AGGREGATION_MODE_DEFAULT) {
396269cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId, aggregationMode, false);
3963f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                }
3964f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            }
3965433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            if (values.containsKey(RawContacts.STARRED)) {
3966dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter) {
3967dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
3968dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            values.getAsLong(RawContacts.STARRED) != 0);
3969dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
39704529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                mContactAggregator.updateStarred(rawContactId);
3971dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
3972dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // if this raw contact is being associated with an account, then update the
3973dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // favorites group membership based on whether or not this contact is starred.
3974dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // If it is starred, add a group membership, if one doesn't already exist
3975dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // otherwise delete any matching group memberships.
3976dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
3977dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    boolean starred = 0 != DatabaseUtils.longForQuery(mDb,
3978dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            SELECTION_STARRED_FROM_RAW_CONTACTS,
3979dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            new String[]{Long.toString(rawContactId)});
3980dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId, starred);
3981dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
3982dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
3983dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3984dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // if this raw contact is being associated with an account, then add a
3985dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // group membership to the group marked as AutoAdd, if any.
3986dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
3987dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                addAutoAddMembership(rawContactId);
3988433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            }
3989dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3990285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (values.containsKey(RawContacts.SOURCE_ID)) {
39912b7a632bba423357ae5641f94da6a2f71afc523bDmitri Plotnikov                mContactAggregator.updateLookupKeyForRawContact(mDb, rawContactId);
3992285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
3993f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            if (values.containsKey(RawContacts.NAME_VERIFIED)) {
3994f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
3995f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // If setting NAME_VERIFIED for this raw contact, reset it for all
3996f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // other raw contacts in the same aggregate
3997f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                if (values.getAsInteger(RawContacts.NAME_VERIFIED) != 0) {
3998f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                    mResetNameVerifiedForOtherRawContacts.bindLong(1, rawContactId);
3999f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                    mResetNameVerifiedForOtherRawContacts.bindLong(2, rawContactId);
4000f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                    mResetNameVerifiedForOtherRawContacts.execute();
4001f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                }
4002f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                mContactAggregator.updateDisplayNameForRawContact(mDb, rawContactId);
4003f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            }
400419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            if (requestUndoDelete && previousDeleted == 1) {
400519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                // undo delete, needs aggregation again.
4006ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                mInsertedRawContacts.put(rawContactId, new Account(accountName, accountType));
400719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
40085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
40095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return count;
401033b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
401133b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
4012321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(Uri uri, ContentValues values, String selection,
4013f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
401420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
401520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
401620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
40175ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
401820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
401920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
402020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
402120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
402220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
4023b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
402420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
402520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
402670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        boolean containsIsSuperPrimary = mValues.containsKey(Data.IS_SUPER_PRIMARY);
402770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        boolean containsIsPrimary = mValues.containsKey(Data.IS_PRIMARY);
402820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
402920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // Remove primary or super primary values being set to 0. This is disallowed by the
403020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // content provider.
403170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (containsIsSuperPrimary && mValues.getAsInteger(Data.IS_SUPER_PRIMARY) == 0) {
403220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            containsIsSuperPrimary = false;
403370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_SUPER_PRIMARY);
403420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
403570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (containsIsPrimary && mValues.getAsInteger(Data.IS_PRIMARY) == 0) {
403620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            containsIsPrimary = false;
403770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_PRIMARY);
403820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
403920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
404097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
404197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
404297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    Data.IS_READ_ONLY + "=0");
404397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
404497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
4045653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        int count = 0;
404620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
4047653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
4048653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // so we don't need to worry about updating data we don't have permission to read.
404914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(uri, DataUpdateQuery.COLUMNS, selection, selectionArgs, null);
4050653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        try {
4051653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            while(c.moveToNext()) {
4052f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count += updateData(mValues, c, callerIsSyncAdapter);
405320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
4054653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        } finally {
4055653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            c.close();
405620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
405720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
4058653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return count;
405920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
406020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
4061f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
4062653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        if (values.size() == 0) {
4063653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return 0;
4064321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
4065653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
406614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        final String mimeType = c.getString(DataUpdateQuery.MIMETYPE);
4067a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
4068813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        if (rowHandler.update(mDb, values, c, callerIsSyncAdapter)) {
4069813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return 1;
4070813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        } else {
4071813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return 0;
4072a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
4073321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    }
4074321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
40758c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(ContentValues values, String selection,
4076dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
40778c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        int count = 0;
4078b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getContactView(),
40798c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                new String[] { Contacts._ID }, selection,
40808c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                selectionArgs, null, null, null);
40818c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        try {
40828c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            while (cursor.moveToNext()) {
40838c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                long contactId = cursor.getLong(0);
4084dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateContactOptions(contactId, values, callerIsSyncAdapter);
40858c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count++;
40868c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            }
40878c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        } finally {
40888c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            cursor.close();
40898c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        }
40908c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
40918c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return count;
40928c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
40938c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
4094dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateContactOptions(long contactId, ContentValues values,
4095dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
4096d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
40978c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
4098b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
4099d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
4100b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
4101d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
4102b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
4103d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
4104b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
4105d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
4106b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
4107d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.STARRED);
4108d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
4109d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
41108c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.size() == 0) {
4111d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
4112d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
4113d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
41148c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.containsKey(RawContacts.STARRED)) {
4115c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            // Mark dirty when changing starred to trigger sync
41168c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            mValues.put(RawContacts.DIRTY, 1);
4117c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey        }
4118c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey
41194da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(contactId);
412097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        mDb.update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=?"
412197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                + " AND " + RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0", mSelectionArgs1);
41228c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
4123dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (mValues.containsKey(RawContacts.STARRED) && !callerIsSyncAdapter) {
4124dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            Cursor cursor = mDb.query(mDbHelper.getRawContactView(),
4125dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[] { RawContacts._ID }, RawContacts.CONTACT_ID + "=?",
4126dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    mSelectionArgs1, null, null, null);
4127dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            try {
4128dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                while (cursor.moveToNext()) {
4129dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    long rawContactId = cursor.getLong(0);
4130dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
4131dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            mValues.getAsLong(RawContacts.STARRED) != 0);
4132dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
4133dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } finally {
4134dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                cursor.close();
4135dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
4136dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
4137dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
41388c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // Copy changeable values to prevent automatically managed fields from
41398c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // being explicitly updated by clients.
41408c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
4141b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
41428c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
4143b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
41448c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
4145b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
41468c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
4147b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
41488c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
4149b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
41508c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.STARRED);
41518c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
41529b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        int rslt = mDb.update(Tables.CONTACTS, mValues, Contacts._ID + "=?", mSelectionArgs1);
41536e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori
41549b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        if (values.containsKey(Contacts.LAST_TIME_CONTACTED) &&
41559b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori                !values.containsKey(Contacts.TIMES_CONTACTED)) {
41569b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
41579b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
41589b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        }
41599b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        return rslt;
4160f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
4161d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
4162127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
4163127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
41640c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId1 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID1);
41650c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId2 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID2);
416680c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
41670c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rawContactId1, rawContactId2;
41680c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (rcId1 < rcId2) {
41690c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId1;
41700c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId2;
41710c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
41720c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId1;
41730c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId2;
4174b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
4175127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
41760c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
41774da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[0] = String.valueOf(rawContactId1);
41784da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[1] = String.valueOf(rawContactId2);
41790c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.delete(Tables.AGGREGATION_EXCEPTIONS,
41804da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    AggregationExceptions.RAW_CONTACT_ID1 + "=? AND "
41814da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    + AggregationExceptions.RAW_CONTACT_ID2 + "=?", mSelectionArgs2);
41820c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
41836bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            ContentValues exceptionValues = new ContentValues(3);
41846bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
41850c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
41860c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
41870c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
41880c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    exceptionValues);
4189127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
4190127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
41913389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
419269cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId1,
419369cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
419469cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId2,
419569cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
4196dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov
41970dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId1);
41980dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId2);
4199127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
4200127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
4201127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
4202127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
4203b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
4204b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
420570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    public void onAccountsUpdated(Account[] accounts) {
4206afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov        boolean accountsChanged = updateAccounts(accounts);
4207afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov        if (accountsChanged) {
42081b2a89588e9593756c2627ce1683539f4ffa1e51Dmitri Plotnikov            mContactDirectoryManager.scheduleScanAllPackages(true);
4209afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov        }
4210afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov    }
4211afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov
42123826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    protected void updateAccounts() {
42133826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        AccountManager.get(getContext()).addOnAccountsUpdatedListener(this, null, false);
42143826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        Account[] accounts = AccountManager.get(getContext()).getAccounts();
42153826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        updateAccounts(accounts);
42163826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        updateContactsAccountCount(accounts);
42173826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
42183826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
4219afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov    private boolean updateAccounts(Account[] accounts) {
4220f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao        // TODO : Check the unit test.
4221e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov        boolean accountsChanged = false;
4222627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        HashSet<Account> existingAccounts = new HashSet<Account>();
422370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        mDb.beginTransaction();
422470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        try {
4225dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            findValidAccounts(existingAccounts);
4226743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov
4227743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            // Add a row to the ACCOUNTS table for each new account
4228743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            for (Account account : accounts) {
4229743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                if (!existingAccounts.contains(account)) {
4230e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    accountsChanged = true;
4231743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                    mDb.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME
4232743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            + ", " + RawContacts.ACCOUNT_TYPE + ") VALUES (?, ?)",
4233743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            new String[] {account.name, account.type});
4234743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                }
4235743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            }
423648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
4237627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            // Remove all valid accounts from the existing account set. What is left
4238743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            // in the accountsToDelete set will be extra accounts whose data must be deleted.
4239627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            HashSet<Account> accountsToDelete = new HashSet<Account>(existingAccounts);
4240627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (Account account : accounts) {
4241627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                accountsToDelete.remove(account);
424270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
424370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
424433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            if (!accountsToDelete.isEmpty()) {
4245e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                accountsChanged = true;
4246e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                for (Account account : accountsToDelete) {
4247e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    Log.d(TAG, "removing data for removed account " + account);
4248e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    String[] params = new String[] {account.name, account.type};
4249e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4250e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.GROUPS +
4251e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Groups.ACCOUNT_NAME + " = ?" +
4252e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " AND " + Groups.ACCOUNT_TYPE + " = ?", params);
4253e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4254e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.PRESENCE +
4255e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (" +
4256e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    "SELECT " + RawContacts._ID +
4257e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " FROM " + Tables.RAW_CONTACTS +
4258e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
4259e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " AND " + RawContacts.ACCOUNT_TYPE + " = ?)", params);
4260e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4261e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.RAW_CONTACTS +
4262e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
4263e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + " = ?", params);
4264e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4265e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.SETTINGS +
4266e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Settings.ACCOUNT_NAME + " = ?" +
4267e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + Settings.ACCOUNT_TYPE + " = ?", params);
4268e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4269e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.ACCOUNTS +
4270e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + "=?" +
4271e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + "=?", params);
4272d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    mDb.execSQL(
4273d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            "DELETE FROM " + Tables.DIRECTORIES +
4274d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            " WHERE " + Directory.ACCOUNT_NAME + "=?" +
4275d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            " AND " + Directory.ACCOUNT_TYPE + "=?", params);
42764458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    resetDirectoryCache();
4277e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                }
4278e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov
427933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // Find all aggregated contacts that used to contain the raw contacts
428033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // we have just deleted and see if they are still referencing the deleted
4281e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                // names or photos.  If so, fix up those contacts.
428233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                HashSet<Long> orphanContactIds = Sets.newHashSet();
428333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                Cursor cursor = mDb.rawQuery("SELECT " + Contacts._ID +
428433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " FROM " + Tables.CONTACTS +
428533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " WHERE (" + Contacts.NAME_RAW_CONTACT_ID + " NOT NULL AND " +
428669cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                Contacts.NAME_RAW_CONTACT_ID + " NOT IN " +
428769cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + RawContacts._ID +
428869cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.RAW_CONTACTS + "))" +
428933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " OR (" + Contacts.PHOTO_ID + " NOT NULL AND " +
429033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                                Contacts.PHOTO_ID + " NOT IN " +
429169cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + Data._ID +
429269cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.DATA + "))", null);
429333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                try {
429433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    while (cursor.moveToNext()) {
429533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        orphanContactIds.add(cursor.getLong(0));
429633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    }
429733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                } finally {
429833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    cursor.close();
429933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
430033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
430133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                for (Long contactId : orphanContactIds) {
430233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    mContactAggregator.updateAggregateData(contactId);
430333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
4304e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                mDbHelper.updateAllVisible();
430533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            }
430633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
4307e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            if (accountsChanged) {
4308e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                mDbHelper.getSyncState().onAccountsChanged(mDb, accounts);
4309e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            }
431070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.setTransactionSuccessful();
431170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } finally {
431270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.endTransaction();
431370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
431473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.clear();
43153826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
43163826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (accountsChanged) {
43173826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateContactsAccountCount(accounts);
43183826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
43193826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
43203826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
4321afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov        return accountsChanged;
432270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
4323619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
43243826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateContactsAccountCount(Account[] accounts) {
43253826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        int count = 0;
43263826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        for (Account account : accounts) {
43273826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            if (isContactsAccount(account)) {
43283826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                count++;
43293826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            }
43303826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
43313826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mContactsAccountCount = count;
43323826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
43333826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
43343826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    protected boolean isContactsAccount(Account account) {
43353826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        final IContentService cs = ContentResolver.getContentService();
43363826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        try {
43373826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return cs.getIsSyncable(account, ContactsContract.AUTHORITY) > 0;
43383826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } catch (RemoteException e) {
43393826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            Log.e(TAG, "Cannot obtain sync flag for account: " + account, e);
43403826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return false;
43413826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
43423826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
43433826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
434472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void onPackageChanged(String packageName) {
434572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        mContactDirectoryManager.onPackageChanged(packageName);
4346d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4347d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4348619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
4349627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     * Finds all distinct accounts present in the specified table.
4350627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     */
4351dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void findValidAccounts(Set<Account> validAccounts) {
4352743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov        Cursor c = mDb.rawQuery(
4353743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                "SELECT " + RawContacts.ACCOUNT_NAME + "," + RawContacts.ACCOUNT_TYPE +
4354743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                " FROM " + Tables.ACCOUNTS, null);
4355627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
4356627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            while (c.moveToNext()) {
4357dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!c.isNull(0) || !c.isNull(1)) {
4358627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    validAccounts.add(new Account(c.getString(0), c.getString(1)));
4359627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
4360627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
4361627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } finally {
4362627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            c.close();
4363627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
4364627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
4365627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
4366627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    /**
4367622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey     * Test all against {@link TextUtils#isEmpty(CharSequence)}.
4368622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey     */
436967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    private static boolean areAllEmpty(ContentValues values, String[] keys) {
437067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        for (String key : keys) {
437167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            if (!TextUtils.isEmpty(values.getAsString(key))) {
437267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                return false;
437367c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            }
437467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        }
437567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        return true;
437667c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    }
437767c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov
437867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    /**
437967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov     * Returns true if a value (possibly null) is specified for at least one of the supplied keys.
438067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov     */
4381dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov    private static boolean areAnySpecified(ContentValues values, String[] keys) {
4382622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        for (String key : keys) {
4383dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov            if (values.containsKey(key)) {
4384dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov                return true;
4385622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
4386622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
4387dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        return false;
4388622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    }
4389622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
43904f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
43914f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
43924f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
4393d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String directory = getQueryParameter(uri, ContactsContract.DIRECTORY_PARAM_KEY);
4394385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directory == null) {
4395385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            return queryLocal(uri, projection, selection, selectionArgs, sortOrder, -1);
4396385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directory.equals("0")) {
4397385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            return queryLocal(uri, projection, selection, selectionArgs, sortOrder,
4398385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                    Directory.DEFAULT);
4399d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        } else if (directory.equals("1")) {
4400385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            return queryLocal(uri, projection, selection, selectionArgs, sortOrder,
4401385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                    Directory.LOCAL_INVISIBLE);
4402d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4403d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4404d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        DirectoryInfo directoryInfo = getDirectoryAuthority(directory);
4405d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo == null) {
4406a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            Log.e(TAG, "Invalid directory ID: " + uri);
4407a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            return null;
4408d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4409d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4410d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Builder builder = new Uri.Builder();
4411d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.scheme(ContentResolver.SCHEME_CONTENT);
4412d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.authority(directoryInfo.authority);
4413d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.encodedPath(uri.getEncodedPath());
4414d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountName != null) {
4415d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_NAME, directoryInfo.accountName);
4416d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4417d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountType != null) {
4418d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, directoryInfo.accountType);
4419d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
44202e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
44212e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limit = getLimit(uri);
44222e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        if (limit != null) {
44232e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov            builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, limit);
44242e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        }
44252e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
4426d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Uri directoryUri = builder.build();
442709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
442809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        if (projection == null) {
442909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            projection = getDefaultProjection(uri);
443009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
443109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
4432332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        Cursor cursor = getContext().getContentResolver().query(directoryUri, projection, selection,
4433d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                selectionArgs, sortOrder);
4434332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        while (cursor instanceof CursorWrapper) {
4435332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov            cursor = ((CursorWrapper)cursor).getWrappedCursor();
4436332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        }
4437332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        return cursor;
4438d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4439d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4440d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final class DirectoryQuery {
4441d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
4442d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory._ID,
4443d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.DIRECTORY_AUTHORITY,
4444d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_NAME,
4445d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_TYPE
4446d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        };
4447d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4448d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int DIRECTORY_ID = 0;
4449d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int AUTHORITY = 1;
4450d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_NAME = 2;
4451d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_TYPE = 3;
4452d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4453d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4454d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
4455d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Reads and caches directory information for the database.
4456d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
4457d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private DirectoryInfo getDirectoryAuthority(String directoryId) {
44584458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized (mDirectoryCache) {
44594458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            if (!mDirectoryCacheValid) {
44604458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCache.clear();
44614458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                Cursor cursor = mDb.query(Tables.DIRECTORIES,
44624458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryQuery.COLUMNS,
44634458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        null, null, null, null, null);
44644458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                try {
44654458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    while (cursor.moveToNext()) {
44664458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryInfo info = new DirectoryInfo();
44674458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        String id = cursor.getString(DirectoryQuery.DIRECTORY_ID);
44684458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.authority = cursor.getString(DirectoryQuery.AUTHORITY);
44694458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
44704458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
44714458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        mDirectoryCache.put(id, info);
44724458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    }
44734458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                } finally {
44744458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    cursor.close();
4475d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                }
44764458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCacheValid = true;
4477d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4478d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
44794458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            return mDirectoryCache.get(directoryId);
44804458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
4481d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4482d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
448372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void resetDirectoryCache() {
44844458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized(mDirectoryCache) {
44854458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            mDirectoryCacheValid = false;
44864458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
448772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
448872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
4489d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    public Cursor queryLocal(Uri uri, String[] projection, String selection, String[] selectionArgs,
4490385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                String sortOrder, long directoryId) {
4491bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
4492bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            Log.v(TAG, "query: " + uri);
4493bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        }
44940b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov
4495b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
449635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4497d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
44981f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
4499c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limit = getLimit(uri);
4500c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4501619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // TODO: Consider writing a test case for RestrictionExceptions when you
4502619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // write a new query() block to make sure it protects restricted data.
4503a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
45044f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
450535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
4506b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().query(db, projection, selection,  selectionArgs,
450735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                        sortOrder);
450835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4509d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
4510763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
4511385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                appendLocalDirectorySelectionIfNeeded(qb, directoryId);
4512619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
4513619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
4514619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
4515d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
45164a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
4517763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
45184da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
45194da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
45206bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
45216bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
45226bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
45235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP:
45245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
45255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
45265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int segmentCount = pathSegments.size();
45275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount < 3) {
4528fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
4529fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
45305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
4531a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
45325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String lookupKey = pathSegments.get(2);
45335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount == 4) {
45345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
45355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
4536763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
4537a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4538a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
4539a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4540a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
4541a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
45425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        return c;
45435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
45445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
45455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
4546763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
45474da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
45484da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
45494da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
45505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                break;
45515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
45525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
45532149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_DATA:
45542149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_DATA: {
45552149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
45562149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                int segmentCount = pathSegments.size();
45572149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount < 4) {
45582149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
45592149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                            "Missing a lookup key", uri));
45602149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
45612149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
45622149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount == 5) {
45632149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
45642149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
45652149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    setTablesAndProjectionMapForData(lookupQb, uri, projection, false);
4566a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
4567a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
4568a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4569a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Data.CONTACT_ID, contactId, Data.LOOKUP_KEY, lookupKey);
4570a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
45712149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                        return c;
45722149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    }
45732149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
45742149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    // TODO see if the contact exists but has no data rows (rare)
45752149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
45762149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
45772149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
45782149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
45792149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
45802149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                qb.appendWhere(" AND " + Data.CONTACT_ID + "=?");
45812149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                break;
45822149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            }
45832149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
4584f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
4585f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                // When reading as vCard always use restricted view
458642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
4587763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                qb.setTables(mDbHelper.getContactView(true /* require restricted */));
4588f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                qb.setProjectionMap(sContactsVCardProjectionMap);
45894da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
45904da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
45914da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
4592f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                break;
4593f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            }
4594f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
459542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
459642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
459742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                String currentDateString = dateFormat.format(new Date()).toString();
459842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                return db.rawQuery(
459942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    "SELECT" +
460042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " 'vcards_' || ? || '.vcf' AS " + OpenableColumns.DISPLAY_NAME + "," +
460142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " NULL AS " + OpenableColumns.SIZE,
460242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    new String[] { currentDateString });
460342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
460442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
4605ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_FILTER: {
4606916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                String filterParam = "";
4607ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
4608916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    filterParam = uri.getLastPathSegment();
4609ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
4610916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                setTablesAndProjectionMapForContactsWithSnippet(qb, uri, projection, filterParam);
4611385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                appendLocalDirectorySelectionIfNeeded(qb, directoryId);
4612ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4613ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4614ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
4615ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT_FILTER:
4616ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT: {
46174a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                String filterSql = null;
4618ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                if (match == CONTACTS_STREQUENT_FILTER
4619d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        && uri.getPathSegments().size() > 3) {
46204a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
46214a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
4622e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
46235e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
46244a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    filterSql = sb.toString();
46254a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
46264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
4627763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
4628ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
46295e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] starredProjection = null;
46305e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] frequentProjection = null;
46315e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                if (projection != null) {
4632dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                    starredProjection =
4633dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                            appendProjectionArg(projection, TIMES_CONTACTED_SORT_COLUMN);
4634dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                    frequentProjection =
4635dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                            appendProjectionArg(projection, TIMES_CONTACTED_SORT_COLUMN);
46365e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                }
46375e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
46384a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                // Build the first query for starred
46394a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
46404a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
4641d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
46425e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                qb.setProjectionMap(sStrequentStarredProjectionMap);
46435e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                final String starredQuery = qb.buildQuery(starredProjection, Contacts.STARRED + "=1",
46444a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
4645d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
4646d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Build the second query for frequent
4647d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
4648763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
46494a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
46504a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
4651d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
46525e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                qb.setProjectionMap(sStrequentFrequentProjectionMap);
46535e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                final String frequentQuery = qb.buildQuery(frequentProjection,
4654d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        Contacts.TIMES_CONTACTED + " > 0 AND (" + Contacts.STARRED
4655d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        + " = 0 OR " + Contacts.STARRED + " IS NULL)",
46564a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
4657d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
4658d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
4659d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String query = qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
4660d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        STREQUENT_ORDER_BY, STREQUENT_LIMIT);
46614a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Cursor c = db.rawQuery(query, null);
46624a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (c != null) {
4663d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    c.setNotificationUri(getContext().getContentResolver(),
4664d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
4665d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
4666d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                return c;
4667d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
4668d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
4669ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_GROUP: {
4670763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
4671b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
467271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
46734a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
4674b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
4675b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
4676b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
4677b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
4678a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_DATA: {
46794a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
468082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
46814da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
46824da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
46836bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
46846bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
468500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
4686a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
46873653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
468882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
46894da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
46904da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
46913653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
46923653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                break;
46933653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
46943653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
4695a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_ENTITIES: {
4696a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
4697a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
4698a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
4699a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
4700a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
4701a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
4702a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4703a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ENTITIES:
4704a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_ENTITIES: {
4705a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
4706a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                int segmentCount = pathSegments.size();
4707a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount < 4) {
4708a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
4709a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            "Missing a lookup key", uri));
4710a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
4711a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
4712a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount == 5) {
4713a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
4714a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
4715a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    setTablesAndProjectionMapForEntities(lookupQb, uri, projection);
4716a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
4717a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4718a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
4719a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4720a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.CONTACT_ID, contactId,
4721a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.LOOKUP_KEY, lookupKey);
4722a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
4723a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        return c;
4724a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    }
4725a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
4726a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4727a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
4728a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
4729a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
4730a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + Contacts.Entity.CONTACT_ID + "=?");
4731a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
4732a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
4733a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
47344a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case PHONES: {
473582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
473689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
47372815f58f72f109790585931f601a63ddc02536a5Evan Millar                break;
47382815f58f72f109790585931f601a63ddc02536a5Evan Millar            }
47392815f58f72f109790585931f601a63ddc02536a5Evan Millar
474048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID: {
474182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
47424da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
474348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
47444da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
474548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
474648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
474748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
4748ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
474982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, true);
475089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
4751ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
47524a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
47534a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
4754a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    sb.append(" AND (");
47555e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
475645d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    boolean hasCondition = false;
47575e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    boolean orNeeded = false;
47585e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String normalizedName = NameNormalizer.normalize(filterParam);
47595e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (normalizedName.length() > 0) {
47605e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN ");
47617318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov                        appendRawContactsByNormalizedNameFilter(sb, normalizedName, false);
47625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        orNeeded = true;
476345d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
47645e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
47655e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
4766892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    String number = PhoneNumberUtils.normalizeNumber(filterParam);
4767892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (!TextUtils.isEmpty(number)) {
47685e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        if (orNeeded) {
47695e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                            sb.append(" OR ");
47705e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        }
47715e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data._ID +
4772892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                " IN (SELECT DISTINCT " + PhoneLookupColumns.DATA_ID
4773892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " FROM " + Tables.PHONE_LOOKUP
4774892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
4775892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append(number);
4776892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append("%')");
477745d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
477845d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    }
477945d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov
478045d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    if (!hasCondition) {
478145d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // If it is neither a phone number nor a name, the query should return
478245d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // an empty cursor.  Let's ensure that.
478345d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        sb.append("0");
47845e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
47855e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
4786a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
4787ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
47885e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = PhoneColumns.NORMALIZED_NUMBER + "," + RawContacts.CONTACT_ID;
4789a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
4790a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                    sortOrder = Contacts.IN_VISIBLE_GROUP + " DESC, " + RawContacts.CONTACT_ID;
4791a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
4792ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4793ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4794ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
47954a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case EMAILS: {
479682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
479789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
47984a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                break;
47994a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
48004a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
480148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID: {
480282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
48034da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
48044da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'"
48054da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        + " AND " + Data._ID + "=?");
480648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
480748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
480848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
48095e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_LOOKUP: {
481082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
481189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
48124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
481308768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String email = uri.getLastPathSegment();
481408768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String address = mDbHelper.extractAddressFromEmailAddress(email);
481508768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, address);
481608768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    qb.appendWhere(" AND UPPER(" + Email.DATA + ")=UPPER(?)");
48174a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
4818ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4819ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4820ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
48215e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_FILTER: {
482282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, true);
482307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                String filterParam = null;
482407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
482507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    filterParam = uri.getLastPathSegment();
482607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    if (TextUtils.isEmpty(filterParam)) {
482707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                        filterParam = null;
482807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    }
482907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                }
48305e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
483107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (filterParam == null) {
483207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    // If the filter is unspecified, return nothing
483307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    qb.appendWhere(" AND 0");
483407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                } else {
483507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    StringBuilder sb = new StringBuilder();
483607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(" AND " + Data._ID + " IN (");
483707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(
483807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            "SELECT " + Data._ID +
483907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " FROM " + Tables.DATA +
484007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " WHERE " + DataColumns.MIMETYPE_ID + "=" + mMimeTypeIdEmail +
484107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " AND " + Data.DATA1 + " LIKE ");
484207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(sb, filterParam + '%');
484320938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                    if (!filterParam.contains("@")) {
484420938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        String normalizedName = NameNormalizer.normalize(filterParam);
484520938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        if (normalizedName.length() > 0) {
484607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov
484707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            /*
484807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * Using a UNION instead of an "OR" to make SQLite use the right
484907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * indexes. We need it to use the (mimetype,data1) index for the
485007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * email lookup (see above), but not for the name lookup.
485107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * SQLite is not smart enough to use the index on one side of an OR
485207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * but not on the other. Using two separate nested queries
485307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * and a UNION between them does the job.
485407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             */
485507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            sb.append(
485607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " UNION SELECT " + Data._ID +
485707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " FROM " + Tables.DATA +
485807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " WHERE +" + DataColumns.MIMETYPE_ID + "=" + mMimeTypeIdEmail +
485907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " AND " + Data.RAW_CONTACT_ID + " IN ");
48607318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov                            appendRawContactsByNormalizedNameFilter(sb, normalizedName, false);
486120938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        }
48625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
48635e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
4864a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
48655e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                }
48665e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
4867a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
4868c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov                    sortOrder = EMAIL_FILTER_SORT_ORDER;
4869a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
48705e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                break;
48715e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
48725e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
4873ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
487482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
487589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
487689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
4877ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4878ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4879ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
488048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
488182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
48824da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
488348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
488448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
48854da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
488648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
488748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
488848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
48895ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
4890763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
48914f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
48924f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
48934f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
48945ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
48955ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
4896763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
48974da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
48984da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
48994f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
49004f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
49014f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
49025ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
49035ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
490482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
49054da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
49064da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=?");
4907e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
4908e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
4909e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
4910e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            case DATA: {
491182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
4912e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
4913e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
4914e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
49154f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case DATA_ID: {
491682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
49174da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
49184da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
4919a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                break;
4920a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            }
4921a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov
4922a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
49234a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
4924a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
4925a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
4926a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
4927892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    sortOrder = " length(lookup.normalized_number) DESC";
4928a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
4929a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
4930e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
4931892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                String numberE164 =
4932892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        PhoneNumberUtils.formatNumberToE164(number, getCurrentCountryIso());
4933892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                String normalizedNumber =
4934892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        PhoneNumberUtils.normalizeNumber(number);
4935892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                mDbHelper.buildPhoneLookupAndContactQuery(qb, normalizedNumber, numberE164);
4936e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                qb.setProjectionMap(sPhoneLookupProjectionMap);
4937e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                // Phone lookup cannot be combined with a selection
4938e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selection = null;
4939e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selectionArgs = null;
4940a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
4941a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
4942a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
4943ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
4944b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
4945ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
494689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
4947ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
4948ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
4949ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
4950ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
4951b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
4952ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
49534da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
49544da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Groups._ID + "=?");
4955ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
4956ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
4957ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
4958ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
4959b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView() + " AS groups");
4960ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsSummaryProjectionMap);
496189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
496289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                groupBy = Groups._ID;
4963ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
4964ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
4965ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
4966b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
49670c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
4968b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
4969b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
4970b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
4971b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
497231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
4973d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
49742d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                String filter = null;
49752d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
49762d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                    filter = uri.getPathSegments().get(3);
49772d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                }
497831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
4979d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                if (limit != null) {
4980d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(limit);
498131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
498231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
498331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
498431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
49855b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                ArrayList<AggregationSuggestionParameter> parameters = null;
49865b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                List<String> query = uri.getQueryParameters("query");
49875b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                if (query != null && !query.isEmpty()) {
49885b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    parameters = new ArrayList<AggregationSuggestionParameter>(query.size());
49895b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    for (String parameter : query) {
49905b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        int offset = parameter.indexOf(':');
49915b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        parameters.add(offset == -1
49925b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                ? new AggregationSuggestionParameter(
499376dfa406e2cde19c824983c37fc92c1c5bf63eecDaniel Lehmann                                        AggregationSuggestions.PARAMETER_MATCH_NAME,
49945b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter)
49955b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                : new AggregationSuggestionParameter(
49965b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(0, offset),
49975b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(offset + 1)));
49985b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    }
49995b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                }
50005b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
5001763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
50027581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov
50037581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov                return mContactAggregator.queryAggregationSuggestions(qb, projection, contactId,
50045b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        maxSuggestions, filter, parameters);
500531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
500631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
5007eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
5008eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setTables(Tables.SETTINGS);
5009eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setProjectionMap(sSettingsProjectionMap);
501089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
5011e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
5012e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // When requesting specific columns, this query requires
5013e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // late-binding of the GroupMembership MIME-type.
5014b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                final String groupMembershipMimetypeId = Long.toString(mDbHelper
5015e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
501682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
5017b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_COUNT)) {
5018e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
5019e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
502082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
5021b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_WITH_PHONES)) {
5022e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
5023e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
5024e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
5025eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
5026eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
5027eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
502882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
50290a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
50305ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
50315ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
50325ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
503382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES_ID: {
50340a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
50354da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
50364da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(DataColumns.CONCRETE_ID + "=?");
50375ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
50385ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
50395ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
5040c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS: {
5041a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(db, uri, limit);
5042c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5043c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
5044c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT: {
50452d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                String lookupKey = uri.getLastPathSegment();
50462d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                return mGlobalSearchSupport.handleSearchShortcutRefresh(db, lookupKey, projection);
5047c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5048c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
50491b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS:
5050b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
50511b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
50521b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
50531b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
50541b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_WITH_PHONES:
5055b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
50561b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
50571b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.HAS_PHONE_NUMBER + "=1");
50581b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
50591b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
50601b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_FAVORITES:
5061b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
50621b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
50631b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.STARRED + "=1");
50641b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
50651b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
50661b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_GROUP_NAME:
5067b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
50681b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
506971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
50701b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
50711b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
50721b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
507346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITIES: {
5074a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
507546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
507646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
507746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
507846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITY_ID: {
507946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
5080a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
50814da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
50824da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
508346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
508446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
508546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
508609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            case PROVIDER_STATUS: {
508709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                return queryProviderStatus(uri, projection);
508809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
508909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5090d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES : {
5091d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
5092d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
5093d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
5094d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
5095d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
5096d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID : {
5097385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                long id = ContentUris.parseId(uri);
5098d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
5099d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
5100385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(id));
5101d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.appendWhere(Directory._ID + "=?");
5102d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
5103d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
5104d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
51057a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            case COMPLETE_NAME: {
51067a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                return completeName(uri, projection);
51077a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
51087a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
51094f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
5110f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
5111c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                        sortOrder, limit);
51124f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
51134f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
51147f786e5cbde9975b9632beb9b6d19eeef8a64cf1Dmitri Plotnikov        qb.setStrictProjectionMap(true);
51157f786e5cbde9975b9632beb9b6d19eeef8a64cf1Dmitri Plotnikov
5116ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        Cursor cursor =
5117ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit);
5118ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (readBooleanQueryParameter(uri, ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, false)) {
5119ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            cursor = bundleLetterCountExtras(cursor, db, qb, selection, selectionArgs, sortOrder);
5120ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5121ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        return cursor;
51225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
51235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
51245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
51255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String selection, String[] selectionArgs, String sortOrder, String groupBy,
51265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String limit) {
5127038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        if (projection != null && projection.length == 1
5128038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                && BaseColumns._COUNT.equals(projection[0])) {
5129038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            qb.setProjectionMap(sCountProjectionMap);
5130038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
51315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
51325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sortOrder, limit);
51334f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
51344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
51354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
51364f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
51374f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
51384f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
513909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    /**
514009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     * Creates a single-row cursor containing the current status of the provider.
514109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     */
514209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private Cursor queryProviderStatus(Uri uri, String[] projection) {
514309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
514409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        RowBuilder row = cursor.newRow();
514509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
514609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            if (ProviderStatus.STATUS.equals(projection[i])) {
514709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mProviderStatus);
514809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            } else if (ProviderStatus.DATA1.equals(projection[i])) {
514909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mEstimatedStorageRequirement);
515009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
515109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        }
515209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        return cursor;
515309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    }
515409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5155a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /**
5156a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * Runs the query with the supplied contact ID and lookup ID.  If the query succeeds,
5157a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * it returns the resulting cursor, otherwise it returns null and the calling
5158a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * method needs to resolve the lookup key and rerun the query.
5159a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     */
5160a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private Cursor queryWithContactIdAndLookupKey(SQLiteQueryBuilder lookupQb,
5161a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            SQLiteDatabase db, Uri uri,
5162a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection, String selection, String[] selectionArgs,
5163a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String sortOrder, String groupBy, String limit,
5164a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn, long contactId, String lookupKeyColumn, String lookupKey) {
5165a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        String[] args;
5166a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (selectionArgs == null) {
5167a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[2];
5168a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        } else {
5169a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[selectionArgs.length + 2];
5170a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
5171a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5172a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[0] = String.valueOf(contactId);
5173a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[1] = Uri.encode(lookupKey);
5174a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        lookupQb.appendWhere(contactIdColumn + "=? AND " + lookupKeyColumn + "=?");
5175a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        Cursor c = query(db, lookupQb, projection, selection, args, sortOrder,
5176a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                groupBy, limit);
5177a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (c.getCount() != 0) {
5178a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return c;
5179a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5180a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5181a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        c.close();
5182a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return null;
5183a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
518409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5185bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov    private static final class AddressBookIndexQuery {
5186bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String LETTER = "letter";
5187bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String TITLE = "title";
5188bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String COUNT = "count";
5189ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5190bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
5191bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                LETTER, TITLE, COUNT
5192ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        };
5193ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5194bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_LETTER = 0;
5195bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_TITLE = 1;
5196bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_COUNT = 2;
5197bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5198de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa        public static final String ORDER_BY = LETTER + " COLLATE " + PHONEBOOK_COLLATOR_NAME;
5199ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
5200ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5201ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    /**
5202ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * Computes counts by the address book index titles and adds the resulting tally
5203ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * to the returned cursor as a bundle of extras.
5204ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     */
5205ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    private Cursor bundleLetterCountExtras(Cursor cursor, final SQLiteDatabase db,
5206ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            SQLiteQueryBuilder qb, String selection, String[] selectionArgs, String sortOrder) {
5207ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortKey;
5208ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5209ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // The sort order suffix could be something like "DESC".
5210ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // We want to preserve it in the query even though we will change
5211ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // the sort column itself.
5212ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortOrderSuffix = "";
5213ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (sortOrder != null) {
5214ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int spaceIndex = sortOrder.indexOf(' ');
5215ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            if (spaceIndex != -1) {
5216ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder.substring(0, spaceIndex);
5217ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortOrderSuffix = sortOrder.substring(spaceIndex);
5218ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            } else {
5219ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder;
5220ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
5221ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } else {
5222ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            sortKey = Contacts.SORT_KEY_PRIMARY;
5223ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5224ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5225bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        String locale = getLocale().toString();
5226ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        HashMap<String, String> projectionMap = Maps.newHashMap();
5227bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.LETTER,
5228bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                "SUBSTR(" + sortKey + ",1,1) AS " + AddressBookIndexQuery.LETTER);
5229bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5230bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        /**
5231bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * Use the GET_PHONEBOOK_INDEX function, which is an android extension for SQLite3,
5232bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * to map the first letter of the sort key to a character that is traditionally
5233bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * used in phonebooks to represent that letter.  For example, in Korean it will
5234bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * be the first consonant in the letter; for Japanese it will be Hiragana rather
5235bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * than Katakana.
5236bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         */
5237ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.TITLE,
5238bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                "GET_PHONEBOOK_INDEX(SUBSTR(" + sortKey + ",1,1),'" + locale + "')"
5239bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                        + " AS " + AddressBookIndexQuery.TITLE);
5240ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.COUNT,
5241ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                "COUNT(" + Contacts._ID + ") AS " + AddressBookIndexQuery.COUNT);
5242ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        qb.setProjectionMap(projectionMap);
5243ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5244f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov        Cursor indexCursor = qb.query(db, AddressBookIndexQuery.COLUMNS, selection, selectionArgs,
5245ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY, null /* having */,
5246ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY + sortOrderSuffix);
5247ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5248ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        try {
5249f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            int groupCount = indexCursor.getCount();
5250ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            String titles[] = new String[groupCount];
5251ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int counts[] = new int[groupCount];
5252bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            int indexCount = 0;
5253bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            String currentTitle = null;
5254bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5255bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // Since GET_PHONEBOOK_INDEX is a many-to-1 function, we may end up
5256bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // with multiple entries for the same title.  The following code
5257bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // collapses those duplicates.
5258ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            for (int i = 0; i < groupCount; i++) {
5259f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov                indexCursor.moveToNext();
5260bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String title = indexCursor.getString(AddressBookIndexQuery.COLUMN_TITLE);
5261bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int count = indexCursor.getInt(AddressBookIndexQuery.COLUMN_COUNT);
5262bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                if (indexCount == 0 || !TextUtils.equals(title, currentTitle)) {
5263bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    titles[indexCount] = currentTitle = title;
5264bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount] = count;
5265bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    indexCount++;
5266bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                } else {
5267bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount - 1] += count;
5268bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                }
5269bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            }
5270bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5271bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            if (indexCount < groupCount) {
5272bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String[] newTitles = new String[indexCount];
5273bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(titles, 0, newTitles, 0, indexCount);
5274bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                titles = newTitles;
5275bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5276bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int[] newCounts = new int[indexCount];
5277bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(counts, 0, newCounts, 0, indexCount);
5278bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                counts = newCounts;
5279ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
5280ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5281ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            final Bundle bundle = new Bundle();
5282ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            bundle.putStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES, titles);
5283f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            bundle.putIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS, counts);
5284ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            return new CursorWrapper(cursor) {
5285ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5286ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                @Override
5287ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                public Bundle getExtras() {
5288ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                    return bundle;
5289ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                }
5290ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            };
5291ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } finally {
5292f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            indexCursor.close();
5293ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5294ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
5295ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
52962d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    /**
529792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Returns the contact Id for the contact identified by the lookupKey.
529892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Robust against changes in the lookup key: if the key has changed, will
529992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * look up the contact by the raw contact IDs or name encoded in the lookup
530092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * key.
53012d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     */
53022d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    public long lookupContactIdByLookupKey(SQLiteDatabase db, String lookupKey) {
53035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactLookupKey key = new ContactLookupKey();
53045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ArrayList<LookupKeySegment> segments = key.parse(lookupKey);
53055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
530692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        long contactId = -1;
530792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_SOURCE_ID)) {
530892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdBySourceIds(db, segments);
530992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
531092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
531192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
531292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
531392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
531492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        boolean hasRawContactIds =
531592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID);
531692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds) {
531792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdByRawContactIds(db, segments);
531892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
531992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
532092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
532192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
532292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
532392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds
532492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                || lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME)) {
53255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            contactId = lookupContactIdByDisplayNames(db, segments);
53265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
53275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
53285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return contactId;
53295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
53305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
53315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupBySourceIdQuery {
53325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
53335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
53345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
53355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
53365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
53375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
53385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.SOURCE_ID
53395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
53405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
53415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
53425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
53435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
53445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int SOURCE_ID = 3;
53455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
53465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
53475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdBySourceIds(SQLiteDatabase db,
53485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
53495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
53505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(RawContacts.SOURCE_ID + " IN (");
53515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
53525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
535392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID) {
53545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
53555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
53565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
53575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
53585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
53595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
53605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
53615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupBySourceIdQuery.TABLE, LookupBySourceIdQuery.COLUMNS,
53625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
53635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
53645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
53655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE);
53665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME);
53675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
53685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
53695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID);
53705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
53715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
537292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID
537392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
53745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(sourceId)) {
53755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupBySourceIdQuery.CONTACT_ID);
53765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
53775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
53785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
53795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
53805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
53815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
53825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
53835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
53845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
53855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
53865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
538792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByRawContactIdQuery {
538892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
53895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
53905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
53915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
53925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
53935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
539492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts._ID,
53955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
53965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
53975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
53985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
53995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
540092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ID = 3;
54015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
54025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
540392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByRawContactIds(SQLiteDatabase db,
540492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            ArrayList<LookupKeySegment> segments) {
540592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
540692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(RawContacts._ID + " IN (");
54075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
54085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
540992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
541092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(segment.rawContactId);
541192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(",");
54125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
54135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
541492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
541592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
54165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
541792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        Cursor c = db.query(LookupByRawContactIdQuery.TABLE, LookupByRawContactIdQuery.COLUMNS,
541892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                 sb.toString(), null, null, null, null);
541992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        try {
542092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            while (c.moveToNext()) {
542192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountType = c.getString(LookupByRawContactIdQuery.ACCOUNT_TYPE);
542292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountName = c.getString(LookupByRawContactIdQuery.ACCOUNT_NAME);
542392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                int accountHashCode =
542492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
542592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String rawContactId = c.getString(LookupByRawContactIdQuery.ID);
542692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
542792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
542892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID
542992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
543092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && segment.rawContactId.equals(rawContactId)) {
543192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        segment.contactId = c.getLong(LookupByRawContactIdQuery.CONTACT_ID);
543292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        break;
543392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    }
543492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                }
543592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
543692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        } finally {
543792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            c.close();
54385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
54395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
544092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return getMostReferencedContactId(segments);
544192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
544292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
544392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByDisplayNameQuery {
544492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
544592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
544692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String COLUMNS[] = {
544792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.CONTACT_ID,
544892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
544992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
545092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME
545192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        };
545292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
545392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int CONTACT_ID = 0;
545492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_TYPE = 1;
545592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_NAME = 2;
545692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int NORMALIZED_NAME = 3;
545792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
545892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
545992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByDisplayNames(SQLiteDatabase db,
546092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
54615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
54625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(NameLookupColumns.NORMALIZED_NAME + " IN (");
54635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
54645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
546592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
546692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
54675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
54685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
54695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
54705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
54715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
54725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
54735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                + " AND " + RawContacts.CONTACT_ID + " NOT NULL");
54745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
54755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupByDisplayNameQuery.TABLE, LookupByDisplayNameQuery.COLUMNS,
54765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
54775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
54785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
54795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE);
54805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME);
54815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
54825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
54835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME);
54845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
54855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
548692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if ((segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
548792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID)
548892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
54895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(name)) {
54905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupByDisplayNameQuery.CONTACT_ID);
54915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
54925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
54935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
54945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
54955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
54965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
54975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
54985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
54995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
55005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
55015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
550292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private boolean lookupKeyContainsType(ArrayList<LookupKeySegment> segments, int lookupType) {
550392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
550492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            LookupKeySegment segment = segments.get(i);
550592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == lookupType) {
550692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return true;
550792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
550892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
550992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
551092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return false;
551192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
551292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
5513ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateLookupKeyForRawContact(SQLiteDatabase db, long rawContactId) {
5514ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov        mContactAggregator.updateLookupKeyForRawContact(db, rawContactId);
5515ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    }
5516ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov
55175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /**
55185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     * Returns the contact ID that is mentioned the highest number of times.
55195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     */
55205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long getMostReferencedContactId(ArrayList<LookupKeySegment> segments) {
55215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Collections.sort(segments);
55225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
55235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long bestContactId = -1;
55245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int bestRefCount = 0;
55255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
55265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = -1;
55275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int count = 0;
55285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
55295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int segmentCount = segments.size();
55305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segmentCount; i++) {
55315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
55325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.contactId != -1) {
55335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segment.contactId == contactId) {
55345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count++;
55355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                } else {
55365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (count > bestRefCount) {
55375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestContactId = contactId;
55385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestRefCount = count;
55395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
55405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    contactId = segment.contactId;
55415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count = 1;
55425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
55435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
55445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
55455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count > bestRefCount) {
55465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return contactId;
55475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
55485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return bestContactId;
55495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
55505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
55515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
5552763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
5553763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            String[] projection) {
555482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5555916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        appendContactsTables(sb, uri, projection);
5556916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setTables(sb.toString());
5557916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionMap);
5558916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    }
5559916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5560916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /**
5561916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * Finds name lookup records matching the supplied filter, picks one arbitrary match per
5562916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * contact and joins that with other contacts tables.
5563916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     */
5564916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private void setTablesAndProjectionMapForContactsWithSnippet(SQLiteQueryBuilder qb, Uri uri,
5565916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            String[] projection, String filter) {
5566916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5567916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
5568916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        appendContactsTables(sb, uri, projection);
5569916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5570916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append(" JOIN (SELECT " +
5571916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                RawContacts.CONTACT_ID + " AS snippet_contact_id");
5572916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5573916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA_ID)) {
5574916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            sb.append(", " + DataColumns.CONCRETE_ID + " AS "
5575916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    + SearchSnippetColumns.SNIPPET_DATA_ID);
5576916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5577916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
55789c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA1)) {
55799c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov            sb.append(", " + Data.DATA1 + " AS " + SearchSnippetColumns.SNIPPET_DATA1);
5580916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5581916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
55829c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA2)) {
55839c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov            sb.append(", " + Data.DATA2 + " AS " + SearchSnippetColumns.SNIPPET_DATA2);
5584916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5585916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
55869c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA3)) {
55879c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov            sb.append(", " + Data.DATA3 + " AS " + SearchSnippetColumns.SNIPPET_DATA3);
55889c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        }
55899c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov
55909c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA4)) {
55919c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov            sb.append(", " + Data.DATA4 + " AS " + SearchSnippetColumns.SNIPPET_DATA4);
5592916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5593916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5594916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_MIMETYPE)) {
5595916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            sb.append(", (" +
5596916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    "SELECT " + MimetypesColumns.MIMETYPE +
5597916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " FROM " + Tables.MIMETYPES +
5598916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " WHERE " + MimetypesColumns._ID + "=" + DataColumns.MIMETYPE_ID +
5599916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    ") AS " + SearchSnippetColumns.SNIPPET_MIMETYPE);
5600916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5601916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5602c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        sb.append(" FROM " + Tables.DATA_JOIN_RAW_CONTACTS + " WHERE ");
5603c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov
5604c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        if (!TextUtils.isEmpty(filter)) {
56056b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov            String normalizedFilter = NameNormalizer.normalize(filter);
56066b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov            if (!TextUtils.isEmpty(normalizedFilter)) {
56076b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                sb.append(DataColumns.CONCRETE_ID + " IN (");
56086b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov
56096b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                // Construct a query that gives us exactly one data _id per matching contact.
56106b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                // MIN stands in for ANY in this context.
56116b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                sb.append(
56126b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                        "SELECT MIN(" + Tables.NAME_LOOKUP + "." + NameLookupColumns.DATA_ID + ")" +
56136b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                        " FROM " + Tables.NAME_LOOKUP +
56146b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                        " JOIN " + Tables.RAW_CONTACTS +
56156b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                        " ON (" + RawContactsColumns.CONCRETE_ID
56166b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                                + "=" + Tables.NAME_LOOKUP + "."
56176b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
56186b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                        " WHERE " + NameLookupColumns.NORMALIZED_NAME + " GLOB '");
56196b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                sb.append(normalizedFilter);
56206b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                sb.append("*' AND " + NameLookupColumns.NAME_TYPE +
56216b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                            " IN(" + CONTACT_LOOKUP_NAME_TYPES + ")" +
56226b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                        " GROUP BY " + RawContactsColumns.CONCRETE_CONTACT_ID +
56236b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                        ")");
56246b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov            } else {
56256b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                sb.append("0");     // Empty filter - return an empty set
56266b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov            }
5627c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        } else {
5628c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov            sb.append("0");     // Empty filter - return an empty set
5629c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        }
5630c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov
5631c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        sb.append(") ON (" + Contacts._ID + "=snippet_contact_id)");
5632916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5633916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setTables(sb.toString());
5634916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionWithSnippetMap);
5635916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    }
5636916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5637916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private void appendContactsTables(StringBuilder sb, Uri uri, String[] projection) {
5638763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = false;
5639f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
5640763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
5641763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
5642d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton            excludeRestrictedData = !mDbHelper.hasAccessToRestrictedData(requestingPackage);
5643763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
5644763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getContactView(excludeRestrictedData));
5645a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
5646a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
564782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    }
5648ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
5649763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForRawContacts(SQLiteQueryBuilder qb, Uri uri) {
5650763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        StringBuilder sb = new StringBuilder();
5651763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = false;
5652f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
5653763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
5654763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
5655d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton            excludeRestrictedData = !mDbHelper.hasAccessToRestrictedData(requestingPackage);
5656763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
5657763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getRawContactView(excludeRestrictedData));
5658763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setTables(sb.toString());
5659763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setProjectionMap(sRawContactsProjectionMap);
5660763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        appendAccountFromParameter(qb, uri);
5661763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    }
5662763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
5663a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForRawEntities(SQLiteQueryBuilder qb, Uri uri) {
5664a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(mDbHelper.getRawEntitiesView(shouldExcludeRestrictedData(uri)));
5665a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sRawEntityProjectionMap);
566646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        appendAccountFromParameter(qb, uri);
566746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    }
566846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
566982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
567082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String[] projection, boolean distinct) {
567182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5672a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(mDbHelper.getDataView(shouldExcludeRestrictedData(uri)));
567382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sb.append(" data");
567482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
5675a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, RawContacts.CONTACT_ID);
5676a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
5677a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
5678a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
56793296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
568082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
5681f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov
5682f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        boolean useDistinct = distinct
5683f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov                || !mDbHelper.isInProjection(projection, DISTINCT_DATA_PROHIBITING_COLUMNS);
5684f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setDistinct(useDistinct);
5685f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setProjectionMap(useDistinct ? sDistinctDataProjectionMap : sDataProjectionMap);
568682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        appendAccountFromParameter(qb, uri);
5687ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    }
5688ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
56890a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb,
56900a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String[] projection) {
56910a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5692b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        sb.append(mDbHelper.getDataView());
56930a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        sb.append(" data");
5694a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
5695a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
56960a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
5697a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
5698a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sStatusUpdatesProjectionMap);
5699a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5700a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5701a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForEntities(SQLiteQueryBuilder qb, Uri uri,
5702a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection) {
5703a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
5704a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(mDbHelper.getEntitiesView(shouldExcludeRestrictedData(uri)));
5705a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(" data");
5706a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5707a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts.Entity.CONTACT_ID);
5708a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
5709a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, Contacts.Entity.DATA_ID);
5710a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, Contacts.Entity.DATA_ID);
5711a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5712a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
5713a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sEntityProjectionMap);
5714a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendAccountFromParameter(qb, uri);
5715a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5716a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5717a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactStatusUpdateJoin(StringBuilder sb, String[] projection,
5718a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String lastStatusUpdateIdColumn) {
5719a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection,
5720a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS,
5721a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
5722a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
5723a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
5724a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP)) {
5725a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
5726a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    + ContactsStatusUpdatesColumns.ALIAS +
5727a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + lastStatusUpdateIdColumn + "="
5728a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
57290a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
5730a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
57310a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
5732a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataStatusUpdateJoin(StringBuilder sb, String[] projection,
5733a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
5734b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
57350a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS,
57360a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_RES_PACKAGE,
57370a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_ICON,
57380a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_LABEL,
57390a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_TIMESTAMP)) {
57400a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
5741a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdatesColumns.CONCRETE_DATA_ID + "="
5742a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + dataIdColumn + ")");
57430a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
5744a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5745a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5746a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactPresenceJoin(StringBuilder sb, String[] projection,
5747a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn) {
5748a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection,
5749a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_PRESENCE, Contacts.CONTACT_CHAT_CAPABILITY)) {
5750a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
5751a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + contactIdColumn + " = "
5752a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + AggregatedPresenceColumns.CONCRETE_CONTACT_ID + ")");
5753a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5754a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5755a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5756a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataPresenceJoin(StringBuilder sb, String[] projection,
5757a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
5758a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, Data.PRESENCE, Data.CHAT_CAPABILITY)) {
5759a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
5760a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdates.DATA_ID + "=" + dataIdColumn + ")");
5761a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5762a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5763a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5764385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov    private void appendLocalDirectorySelectionIfNeeded(SQLiteQueryBuilder qb, long directoryId) {
5765385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directoryId == Directory.DEFAULT) {
5766385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " IN " + Tables.DEFAULT_DIRECTORY);
5767385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directoryId == Directory.LOCAL_INVISIBLE){
5768385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " NOT IN " + Tables.DEFAULT_DIRECTORY);
5769385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        }
5770385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov    }
5771385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov
5772a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private boolean shouldExcludeRestrictedData(Uri uri) {
5773a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        // Note: currently, "export only" equals to "restricted", but may not in the future.
5774a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        boolean excludeRestrictedData = readBooleanQueryParameter(uri,
5775a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Data.FOR_EXPORT_ONLY, false);
5776a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (excludeRestrictedData) {
5777a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return true;
5778a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5779a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5780a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
5781a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
5782a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (requestingPackage != null) {
5783a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return !mDbHelper.hasAccessToRestrictedData(requestingPackage);
5784a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5785a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5786a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return false;
57870a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
57880a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
57894a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) {
5790f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
5791f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
5792e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5793e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
5794e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
5795e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
5796fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
5797fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
5798e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
5799e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5800e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
5801e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
5802e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
5803e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
58044a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere(RawContacts.ACCOUNT_NAME + "="
58054a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
58064a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
58074a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountType));
58084a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        } else {
58094a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere("1");
58104a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
58114a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
58124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
5813e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    private String appendAccountToSelection(Uri uri, String selection) {
5814f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
5815f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
5816e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5817e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
5818e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
5819e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
5820fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
5821fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
5822e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
5823e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5824e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
5825e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
5826e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
5827e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
5828e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "="
5829e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
5830e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + RawContacts.ACCOUNT_TYPE + "="
5831e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountType));
5832e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (!TextUtils.isEmpty(selection)) {
5833e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(" AND (");
5834e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(selection);
5835e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(')');
5836e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
5837e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selectionSb.toString();
5838e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } else {
5839e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selection;
5840e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
5841e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    }
5842e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
58437e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
5844c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
5845c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *
5846c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
5847c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
5848c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     */
5849f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private String getLimit(Uri uri) {
58502e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limitParam = getQueryParameter(uri, ContactsContract.LIMIT_PARAM_KEY);
5851c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limitParam == null) {
5852c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
5853c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
5854c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // make sure that the limit is a non-negative integer
5855c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        try {
5856c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int l = Integer.parseInt(limitParam);
5857c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (l < 0) {
5858c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
5859c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return null;
5860c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5861c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return String.valueOf(l);
5862c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        } catch (NumberFormatException ex) {
5863c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
5864c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
5865c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
5866c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
5867c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
586800ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey    String getContactsRestrictions() {
5869d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
587070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
587170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        } else {
5872fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return RawContactsColumns.CONCRETE_IS_RESTRICTED + "=0";
587370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
587470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    }
587570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
587670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    public String getContactsRestrictionExceptionAsNestedQuery(String contactIdColumn) {
5877d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
587870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
587967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        } else {
58805ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            return "(SELECT " + RawContacts.IS_RESTRICTED + " FROM " + Tables.RAW_CONTACTS
58815ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + "=" + contactIdColumn + ")=0";
5882619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
5883619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
5884619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
5885b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    @Override
5886f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
5887b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        int match = sUriMatcher.match(uri);
5888b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        switch (match) {
5889a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
5890f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return openPhotoAssetFile(uri, mode,
5891e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        Data._ID + "=" + Contacts.PHOTO_ID + " AND " + RawContacts.CONTACT_ID + "=?",
5892e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        new String[]{uri.getPathSegments().get(1)});
5893e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            }
5894b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5895e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            case DATA_ID: {
5896f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return openPhotoAssetFile(uri, mode,
5897e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        Data._ID + "=? AND " + Data.MIMETYPE + "='" + Photo.CONTENT_ITEM_TYPE + "'",
58984da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        new String[]{uri.getPathSegments().get(1)});
5899d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
5900d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5901f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
590242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
590342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                mSelectionArgs1[0] = String.valueOf(lookupContactIdByLookupKey(mDb, lookupKey));
590442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + "=?";
590542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
590642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // When opening a contact as file, we pass back contents as a
590742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // vCard-encoded stream. We build into a local buffer first,
590842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // then pipe into MemoryFile once the exact size is known.
590942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
591042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                outputRawContactsAsVCard(localStream, selection, mSelectionArgs1);
5911f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
591242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
591342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
591442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
591542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKeys = uri.getPathSegments().get(2);
591642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String[] loopupKeyList = lookupKeys.split(":");
591742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final StringBuilder inBuilder = new StringBuilder();
591842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                int index = 0;
5919d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // SQLite has limits on how many parameters can be used
5920d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // so the IDs are concatenated to a query string here instead
592142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                for (String lookupKey : loopupKeyList) {
592242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    if (index == 0) {
5923d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append("(");
592442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    } else {
5925d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append(",");
592642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    }
5927d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                    inBuilder.append(lookupContactIdByLookupKey(mDb, lookupKey));
592842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    index++;
592942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                }
593042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                inBuilder.append(')');
593142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + " IN " + inBuilder.toString();
5932d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5933d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // When opening a contact as file, we pass back contents as a
5934d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // vCard-encoded stream. We build into a local buffer first,
5935d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // then pipe into MemoryFile once the exact size is known.
5936d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
5937d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                outputRawContactsAsVCard(localStream, selection, null);
5938f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
5939d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
5940b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5941b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            default:
5942fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new FileNotFoundException(mDbHelper.exceptionMessage("File does not exist",
5943fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        uri));
5944b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        }
5945b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
5946b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5947f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor openPhotoAssetFile(Uri uri, String mode, String selection,
5948e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            String[] selectionArgs)
5949e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throws FileNotFoundException {
5950e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        if (!"r".equals(mode)) {
5951e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throw new FileNotFoundException(mDbHelper.exceptionMessage("Mode " + mode
5952e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                    + " not supported.", uri));
5953e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        }
5954e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
5955e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        String sql =
5956e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                "SELECT " + Photo.PHOTO + " FROM " + mDbHelper.getDataView() +
5957e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                " WHERE " + selection;
5958e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        SQLiteDatabase db = mDbHelper.getReadableDatabase();
595908ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        try {
5960f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
5961f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    DatabaseUtils.blobFileDescriptorForQuery(db, sql, selectionArgs));
596208ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        } catch (SQLiteDoneException e) {
596308ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            // this will happen if the DB query returns no rows (i.e. contact does not exist)
596408ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            throw new FileNotFoundException(uri.toString());
596508ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        }
5966e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov    }
5967e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
5968d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String CONTACT_MEMORY_FILE_NAME = "contactAssetFile";
5969d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5970d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
5971f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert     * Returns an {@link AssetFileDescriptor} backed by the
5972d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * contents of the given {@link ByteArrayOutputStream}.
5973d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
5974f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor buildAssetFileDescriptor(ByteArrayOutputStream stream) {
5975d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        try {
5976d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            stream.flush();
5977d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5978d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final byte[] byteData = stream.toByteArray();
5979d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5980f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
5981f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    ParcelFileDescriptor.fromData(byteData, CONTACT_MEMORY_FILE_NAME),
5982f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    byteData.length);
5983d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        } catch (IOException e) {
5984ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            Log.w(TAG, "Problem writing stream into an ParcelFileDescriptor: " + e.toString());
5985ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            return null;
5986d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
5987d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
5988d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5989f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd) {
5990f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return makeAssetFileDescriptor(fd, AssetFileDescriptor.UNKNOWN_LENGTH);
5991f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
5992f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
5993f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd, long length) {
5994f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return fd != null ? new AssetFileDescriptor(fd, 0, length) : null;
5995f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
5996f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
5997d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
5998d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Output {@link RawContacts} matching the requested selection in the vCard
5999d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * format to the given {@link OutputStream}. This method returns silently if
6000d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * any errors encountered.
6001d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
6002d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private void outputRawContactsAsVCard(OutputStream stream, String selection,
6003d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            String[] selectionArgs) {
6004d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final Context context = this.getContext();
60057a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        final VCardComposer composer =
60067a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa                new VCardComposer(context, VCardConfig.VCARD_TYPE_DEFAULT, false);
6007d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.addHandler(composer.new HandlerForOutputStream(stream));
6008d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6009f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        // No extra checks since composer always uses restricted views
60107a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        if (!composer.init(selection, selectionArgs)) {
60117a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa            Log.w(TAG, "Failed to init VCardComposer");
6012d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            return;
60137a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        }
6014d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
6015d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        while (!composer.isAfterLast()) {
6016d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            if (!composer.createOneEntry()) {
6017d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                Log.w(TAG, "Failed to output a contact.");
6018d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
6019d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
6020d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.terminate();
6021d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
6022b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
60234f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
60244f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
6025a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
60264f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
6027b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS:
6028be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return Contacts.CONTENT_TYPE;
60292d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill            case CONTACTS_LOOKUP:
6030b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_ID:
6031b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
6032b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_ITEM_TYPE;
6033f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD:
603442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD:
6035f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                return Contacts.CONTENT_VCARD_TYPE;
6036f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            case CONTACTS_ID_PHOTO:
6037f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                return "image/png";
6038b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS:
6039be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return RawContacts.CONTENT_TYPE;
6040b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS_ID:
6041b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return RawContacts.CONTENT_ITEM_TYPE;
6042f481f22a9323fe338672f99b88b26c5f0725cd42David Brown            case DATA:
6043f481f22a9323fe338672f99b88b26c5f0725cd42David Brown                return Data.CONTENT_TYPE;
6044508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
6045b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getDataMimeType(ContentUris.parseId(uri));
604648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES:
604748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_TYPE;
604848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
604948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_ITEM_TYPE;
60509005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov            case PHONE_LOOKUP:
60519005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov                return PhoneLookup.CONTENT_TYPE;
605248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS:
605348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_TYPE;
605448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
605548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_ITEM_TYPE;
605648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS:
605748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_TYPE;
605848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID:
605948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_ITEM_TYPE;
6060b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
6061b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_TYPE;
6062b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
6063b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_ITEM_TYPE;
6064b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case SETTINGS:
6065b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Settings.CONTENT_TYPE;
6066b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
6067b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_TYPE;
6068c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS:
6069c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SUGGEST_MIME_TYPE;
6070c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT:
6071c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
6072d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES:
6073d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_TYPE;
6074d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID:
6075d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_ITEM_TYPE;
607661efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov            default:
607761efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov                return mLegacyApiSupport.getType(uri);
60784f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
60794f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
60807e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
608109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    public String[] getDefaultProjection(Uri uri) {
608209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        final int match = sUriMatcher.match(uri);
608309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        switch (match) {
608409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS:
608509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP:
608609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_ID:
608709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
608809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
608909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsProjectionMap.getColumnNames();
609009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
60918727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov            case CONTACTS_ID_ENTITIES:
60928727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov                return sEntityProjectionMap.getColumnNames();
60938727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov
609409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_VCARD:
609509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_MULTI_VCARD:
609609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsVCardProjectionMap.getColumnNames();
609709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
609809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS:
609909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS_ID:
610009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sRawContactsProjectionMap.getColumnNames();
610109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
610209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DATA_ID:
610309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES:
610409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES_ID:
610509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS:
610609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS_ID:
610709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS:
610809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS_ID:
610909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDataProjectionMap.getColumnNames();
611009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
611109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONE_LOOKUP:
611209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sPhoneLookupProjectionMap.getColumnNames();
611309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
611409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
611509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
611609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sAggregationExceptionsProjectionMap.getColumnNames();
611709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
611809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case SETTINGS:
611909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sSettingsProjectionMap.getColumnNames();
612009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
612109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES:
612209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES_ID:
612309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDirectoryProjectionMap.getColumnNames();
612409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
612509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            default:
612609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return null;
612709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
612809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    }
612909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
61305dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    private void setDisplayName(long rawContactId, int displayNameSource,
61315dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            String displayNamePrimary, String displayNameAlternative, String phoneticName,
61325dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            int phoneticNameStyle, String sortKeyPrimary, String sortKeyAlternative) {
61335dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(1, displayNameSource);
61345dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mRawContactDisplayNameUpdate, 2, displayNamePrimary);
61355dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mRawContactDisplayNameUpdate, 3, displayNameAlternative);
61365dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mRawContactDisplayNameUpdate, 4, phoneticName);
61375dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(5, phoneticNameStyle);
61385dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mRawContactDisplayNameUpdate, 6, sortKeyPrimary);
61395dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mRawContactDisplayNameUpdate, 7, sortKeyAlternative);
61405dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(8, rawContactId);
614125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate.execute();
61423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
61433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
614473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    /**
614573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov     * Sets the {@link RawContacts#DIRTY} for the specified raw contact.
614673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov     */
614773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    private void setRawContactDirty(long rawContactId) {
6148a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        mDirtyRawContacts.add(rawContactId);
614973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    }
615073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
6151c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
6152c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to primary, and resets all data records of
6153c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * the same mimetype and under the same contact to not be primary.
6154c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
6155c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
6156c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
6157653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    private void setIsPrimary(long rawContactId, long dataId, long mimeTypeId) {
6158c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(1, dataId);
6159653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetPrimaryStatement.bindLong(2, mimeTypeId);
6160653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetPrimaryStatement.bindLong(3, rawContactId);
6161c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.execute();
6162c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
6163c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
6164c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
6165c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to "super primary", and resets all data
6166c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * records of the same mimetype and under the same aggregate to not be "super primary".
6167c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
6168c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
6169c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
6170653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    private void setIsSuperPrimary(long rawContactId, long dataId, long mimeTypeId) {
6171c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(1, dataId);
6172653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetSuperPrimaryStatement.bindLong(2, mimeTypeId);
6173653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetSuperPrimaryStatement.bindLong(3, rawContactId);
6174c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.execute();
6175c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
6176ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
6177813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov    public String insertNameLookupForEmail(long rawContactId, long dataId, String email) {
6178f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (TextUtils.isEmpty(email)) {
6179813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return null;
6180f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6181f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6182b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov        String address = mDbHelper.extractHandleFromEmailAddress(email);
6183b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov        if (address == null) {
6184813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return null;
6185f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6186f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6187f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        insertNameLookup(rawContactId, dataId,
6188f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NameLookupType.EMAIL_BASED_NICKNAME, NameNormalizer.normalize(address));
6189813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        return address;
6190f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
6191f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6192f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
6193f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Normalizes the nickname and inserts it in the name lookup table.
6194f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
6195f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookupForNickname(long rawContactId, long dataId, String nickname) {
6196f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (TextUtils.isEmpty(nickname)) {
6197f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return;
6198f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6199f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6200f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        insertNameLookup(rawContactId, dataId,
6201f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NameLookupType.NICKNAME, NameNormalizer.normalize(nickname));
6202f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
6203f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6204a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka    public void insertNameLookupForOrganization(long rawContactId, long dataId, String company,
6205a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String title) {
6206a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        if (!TextUtils.isEmpty(company)) {
6207a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookup(rawContactId, dataId,
6208a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    NameLookupType.ORGANIZATION, NameNormalizer.normalize(company));
6209a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        }
6210a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        if (!TextUtils.isEmpty(title)) {
6211a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookup(rawContactId, dataId,
6212a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    NameLookupType.ORGANIZATION, NameNormalizer.normalize(title));
6213a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        }
6214a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka    }
6215f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6216d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov    public void insertNameLookupForStructuredName(long rawContactId, long dataId, String name,
6217d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov            int fullNameStyle) {
6218d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov        mNameLookupBuilder.insertNameLookup(rawContactId, dataId, name, fullNameStyle);
6219f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
6220f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6221f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private class StructuredNameLookupBuilder extends NameLookupBuilder {
6222f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6223f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public StructuredNameLookupBuilder(NameSplitter splitter) {
6224f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            super(splitter);
6225f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6226f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6227f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
6228f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
6229f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name) {
6230f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            ContactsProvider2.this.insertNameLookup(rawContactId, dataId, lookupType, name);
6231f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6232f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6233f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
6234f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected String[] getCommonNicknameClusters(String normalizedName) {
6235d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov            return mCommonNicknameCache.getCommonNicknameClusters(normalizedName);
6236f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6237f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
6238f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
623948786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov    public void insertNameLookupForPhoneticName(long rawContactId, long dataId,
624048786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            ContentValues values) {
624148786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        if (values.containsKey(StructuredName.PHONETIC_FAMILY_NAME)
624248786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                || values.containsKey(StructuredName.PHONETIC_GIVEN_NAME)
624348786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                || values.containsKey(StructuredName.PHONETIC_MIDDLE_NAME)) {
624448786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            insertNameLookupForPhoneticName(rawContactId, dataId,
624548786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                    values.getAsString(StructuredName.PHONETIC_FAMILY_NAME),
624648786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                    values.getAsString(StructuredName.PHONETIC_MIDDLE_NAME),
624748786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                    values.getAsString(StructuredName.PHONETIC_GIVEN_NAME));
624848786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        }
624948786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov    }
625048786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov
625148786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov    public void insertNameLookupForPhoneticName(long rawContactId, long dataId, String familyName,
625248786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            String middleName, String givenName) {
625348786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        mSb.setLength(0);
625448786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        if (familyName != null) {
625548786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            mSb.append(familyName.trim());
625648786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        }
625748786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        if (middleName != null) {
625848786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            mSb.append(middleName.trim());
625948786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        }
626048786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        if (givenName != null) {
626148786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            mSb.append(givenName.trim());
626248786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        }
626348786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov
626448786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        if (mSb.length() > 0) {
626548786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            insertNameLookup(rawContactId, dataId, NameLookupType.NAME_COLLATION_KEY,
626648786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                    NameNormalizer.normalize(mSb.toString()));
626748786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        }
62683b10d3a1ed1052dcdf529da370cb71b74164b158Dmitri Plotnikov
62693b10d3a1ed1052dcdf529da370cb71b74164b158Dmitri Plotnikov        if (givenName != null) {
62703b10d3a1ed1052dcdf529da370cb71b74164b158Dmitri Plotnikov            // We want the phonetic given name to be used for search, but not for aggregation,
62713b10d3a1ed1052dcdf529da370cb71b74164b158Dmitri Plotnikov            // which is why we are using NAME_SHORTHAND rather than NAME_COLLATION_KEY
62723b10d3a1ed1052dcdf529da370cb71b74164b158Dmitri Plotnikov            insertNameLookup(rawContactId, dataId, NameLookupType.NAME_SHORTHAND,
62733b10d3a1ed1052dcdf529da370cb71b74164b158Dmitri Plotnikov                    NameNormalizer.normalize(givenName.trim()));
62743b10d3a1ed1052dcdf529da370cb71b74164b158Dmitri Plotnikov        }
627548786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov    }
627648786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov
6277f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
6278f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Inserts a record in the {@link Tables#NAME_LOOKUP} table.
6279f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
6280f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookup(long rawContactId, long dataId, int lookupType, String name) {
62815dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mNameLookupInsert.bindLong(1, rawContactId);
62825dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mNameLookupInsert.bindLong(2, dataId);
62835dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mNameLookupInsert.bindLong(3, lookupType);
62845dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mNameLookupInsert, 4, name);
6285f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupInsert.executeInsert();
6286f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
6287f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6288f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
6289f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Deletes all {@link Tables#NAME_LOOKUP} table rows associated with the specified data element.
6290f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
6291f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void deleteNameLookup(long dataId) {
62925dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mNameLookupDelete.bindLong(1, dataId);
6293f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupDelete.execute();
6294f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
6295f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
62962d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov    public void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
6297d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
6298d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + RawContacts.CONTACT_ID +
6299d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.RAW_CONTACTS +
6300d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " JOIN " + Tables.NAME_LOOKUP +
6301d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " ON(" + RawContactsColumns.CONCRETE_ID + "="
6302d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
6303d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE normalized_name GLOB '");
6304e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
6305916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE +
6306916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " IN(" + CONTACT_LOOKUP_NAME_TYPES + "))");
6307e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    }
6308e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
63095ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    public String getRawContactsByFilterAsNestedQuery(String filterParam) {
6310c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        StringBuilder sb = new StringBuilder();
63117318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov        appendRawContactsByFilterAsNestedQuery(sb, filterParam);
6312c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        return sb.toString();
6313c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
6314c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
63157318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov    public void appendRawContactsByFilterAsNestedQuery(StringBuilder sb, String filterParam) {
63167318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov        appendRawContactsByNormalizedNameFilter(sb, NameNormalizer.normalize(filterParam), true);
63175e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    }
63185e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
63195e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private void appendRawContactsByNormalizedNameFilter(StringBuilder sb, String normalizedName,
63207318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov            boolean allowEmailMatch) {
632123061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov        if (TextUtils.isEmpty(normalizedName)) {
632223061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            // Effectively an empty IN clause - SQL syntax does not allow an actual empty list here
632323061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            sb.append("(0)");
632423061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov        } else {
632523061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            sb.append("(" +
632623061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    "SELECT " + NameLookupColumns.RAW_CONTACT_ID +
632723061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    " FROM " + Tables.NAME_LOOKUP +
632823061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    " WHERE " + NameLookupColumns.NORMALIZED_NAME +
632923061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    " GLOB '");
633023061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            // Should not use a "?" argument placeholder here, because
633123061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            // that would prevent the SQL optimizer from using the index on NORMALIZED_NAME.
633223061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            sb.append(normalizedName);
633323061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN ("
633423061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    + NameLookupType.NAME_COLLATION_KEY + ","
633523061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    + NameLookupType.NICKNAME + ","
633623061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    + NameLookupType.NAME_SHORTHAND + ","
633723061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    + NameLookupType.ORGANIZATION + ","
633823061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    + NameLookupType.NAME_CONSONANTS);
633923061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            if (allowEmailMatch) {
634023061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                sb.append("," + NameLookupType.EMAIL_BASED_NICKNAME);
634123061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            }
634223061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            sb.append("))");
634323061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov        }
6344ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    }
6345ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
63464a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /**
63477a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * Takes components of a name from the query parameters and returns a cursor with those
63487a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * components as well as all missing components.  There is no database activity involved
63497a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * in this so the call can be made on the UI thread.
63507a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     */
63517a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private Cursor completeName(Uri uri, String[] projection) {
63527a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        if (projection == null) {
63537a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            projection = sDataProjectionMap.getColumnNames();
63547a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
63557a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
63567a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        ContentValues values = new ContentValues();
63577a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        StructuredNameRowHandler handler =
63587a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                (StructuredNameRowHandler) getDataRowHandler(StructuredName.CONTENT_ITEM_TYPE);
63597a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
63607a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        copyQueryParamsToContentValues(values, uri,
63617a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.DISPLAY_NAME,
63627a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PREFIX,
63637a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.GIVEN_NAME,
63647a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.MIDDLE_NAME,
63657a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.FAMILY_NAME,
63667a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.SUFFIX,
63677a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_NAME,
63687a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_FAMILY_NAME,
63697a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_MIDDLE_NAME,
63707a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_GIVEN_NAME
63717a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        );
63727a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
63737a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        handler.fixStructuredNameComponents(values, values);
63747a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
63757a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
63767a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        Object[] row = new Object[projection.length];
63777a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
63787a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            row[i] = values.get(projection[i]);
63797a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
63807a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        cursor.addRow(row);
63817a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        return cursor;
63827a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
63837a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
63847a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private void copyQueryParamsToContentValues(ContentValues values, Uri uri, String... columns) {
63857a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (String column : columns) {
63867a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            String param = uri.getQueryParameter(column);
63877a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            if (param != null) {
63887a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                values.put(column, param);
63897a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
63907a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
63917a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
63927a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
63937a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
63947a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    /**
63954a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     * Inserts an argument at the beginning of the selection arg list.
63964a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     */
63974a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private String[] insertSelectionArg(String[] selectionArgs, String arg) {
6398b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
6399b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
6400b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
6401b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
6402b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
64034a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            newSelectionArgs[0] = arg;
64044a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length);
6405b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
6406b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
6407b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
6408caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
64095e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private String[] appendProjectionArg(String[] projection, String arg) {
64105e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        if (projection == null) {
64115e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            return null;
64125e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        }
64135e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        final int length = projection.length;
64145e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        String[] newProjection = new String[length + 1];
64155e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        System.arraycopy(projection, 0, newProjection, 0, length);
64165e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        newProjection[length] = arg;
64175e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        return newProjection;
64185e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    }
64195e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
6420caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    protected Account getDefaultAccount() {
6421caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        AccountManager accountManager = AccountManager.get(getContext());
6422caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        try {
6423df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana            Account[] accounts = accountManager.getAccountsByTypeAndFeatures(DEFAULT_ACCOUNT_TYPE,
6424df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                    new String[] {FEATURE_LEGACY_HOSTED_OR_GOOGLE}, null, null).getResult();
6425caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            if (accounts != null && accounts.length > 0) {
6426caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov                return accounts[0];
6427caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            }
6428caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        } catch (Throwable e) {
64296f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov            Log.e(TAG, "Cannot determine the default account for contacts compatibility", e);
6430caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        }
64316f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov        return null;
6432caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    }
6433f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
643473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    /**
643573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     * Returns true if the specified account type is writable.
643673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     */
643773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    protected boolean isWritableAccount(String accountType) {
6438bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        if (accountType == null) {
6439bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov            return true;
6440bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        }
6441bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov
644273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        Boolean writable = mAccountWritability.get(accountType);
644373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable != null) {
644473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            return writable;
644573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
644673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
6447627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        IContentService contentService = ContentResolver.getContentService();
6448627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
6449627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (SyncAdapterType sync : contentService.getSyncAdapterTypes()) {
6450627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (ContactsContract.AUTHORITY.equals(sync.authority) &&
645173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                        accountType.equals(sync.accountType)) {
645273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    writable = sync.supportsUploading();
645373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    break;
6454627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
6455627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
6456627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } catch (RemoteException e) {
6457627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            Log.e(TAG, "Could not acquire sync adapter types");
6458627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
645973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
646073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable == null) {
646173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            writable = false;
646273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
646373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
646473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.put(accountType, writable);
646573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        return writable;
6466627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
6467b4e61e064b59e8076df81b061add9fb358fd2ed9Dmitri Plotnikov
6468d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
6469f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static boolean readBooleanQueryParameter(Uri uri, String parameter,
6470f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean defaultValue) {
6471f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6472f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        // Manually parse the query, which is much faster than calling uri.getQueryParameter
6473f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
6474f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
6475f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
6476f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6477f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6478f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = query.indexOf(parameter);
6479f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (index == -1) {
6480f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
6481f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6482f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6483f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        index += parameter.length();
6484f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6485f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return !matchQueryParameter(query, index, "=0", false)
6486f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && !matchQueryParameter(query, index, "=false", true);
6487f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
6488f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6489f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private static boolean matchQueryParameter(String query, int index, String value,
6490f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean ignoreCase) {
6491f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int length = value.length();
6492f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return query.regionMatches(ignoreCase, index, value, 0, length)
6493f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && (query.length() == index + length || query.charAt(index + length) == '&');
6494f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
6495f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6496f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /**
6497f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * A fast re-implementation of {@link Uri#getQueryParameter}
6498f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     */
6499f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static String getQueryParameter(Uri uri, String parameter) {
6500f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
6501f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
6502f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return null;
6503f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6504f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6505f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int queryLength = query.length();
6506f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int parameterLength = parameter.length();
6507f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6508f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String value;
6509f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = 0;
6510f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        while (true) {
6511f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index = query.indexOf(parameter, index);
6512f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (index == -1) {
6513f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
6514f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
6515f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6516f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index += parameterLength;
6517f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6518f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (queryLength == index) {
6519f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
6520f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
6521f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6522f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (query.charAt(index) == '=') {
6523f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                index++;
6524f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                break;
6525f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
6526f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6527f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6528f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int ampIndex = query.indexOf('&', index);
6529f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (ampIndex == -1) {
6530f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index);
6531f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        } else {
6532f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index, ampIndex);
6533f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6534f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6535f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return Uri.decode(value);
6536f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
65375dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
65385dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    private void bindString(SQLiteStatement stmt, int index, String value) {
65395dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        if (value == null) {
65405dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            stmt.bindNull(index);
65415dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        } else {
65425dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            stmt.bindString(index, value);
65435dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        }
65445dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
65455dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
65465dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    private void bindLong(SQLiteStatement stmt, int index, Number value) {
65475dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        if (value == null) {
65485dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            stmt.bindNull(index);
65495dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        } else {
65505dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            stmt.bindLong(index, value.longValue());
65515dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        }
65525dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
65530dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
65540dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    protected boolean isAggregationUpgradeNeeded() {
65550dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
65560dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            return false;
65570dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
65580dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
65590dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int version = Integer.parseInt(mDbHelper.getProperty(PROPERTY_AGGREGATION_ALGORITHM, "1"));
65600dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        return version < PROPERTY_AGGREGATION_ALGORITHM_VERSION;
65610dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
65620dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
65630dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    protected void upgradeAggregationAlgorithm() {
65640dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // This upgrade will affect very few contacts, so it can be performed on the
65650dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // main thread during the initial boot after an OTA
65660dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
65670dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        Log.i(TAG, "Upgrading aggregation algorithm");
65680dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int count = 0;
65690dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        long start = SystemClock.currentThreadTimeMillis();
65700dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        try {
65710dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.beginTransaction();
65720dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            Cursor cursor = mDb.query(true,
65730dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    Tables.RAW_CONTACTS + " r1 JOIN " + Tables.RAW_CONTACTS + " r2",
65740dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    new String[]{"r1." + RawContacts._ID},
65750dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    "r1." + RawContacts._ID + "!=r2." + RawContacts._ID +
65760dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.CONTACT_ID + "=r2." + RawContacts.CONTACT_ID +
65770dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.ACCOUNT_NAME + "=r2." + RawContacts.ACCOUNT_NAME +
65780dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.ACCOUNT_TYPE + "=r2." + RawContacts.ACCOUNT_TYPE,
65790dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    null, null, null, null, null);
65800dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            try {
65810dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                while (cursor.moveToNext()) {
65820dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    long rawContactId = cursor.getLong(0);
65830dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId,
65840dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                            RawContacts.AGGREGATION_MODE_DEFAULT, true);
65850dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    count++;
65860dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                }
65870dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            } finally {
65880dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                cursor.close();
65890dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            }
65900dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mContactAggregator.aggregateInTransaction(mDb);
65910dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.setTransactionSuccessful();
65920dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDbHelper.setProperty(PROPERTY_AGGREGATION_ALGORITHM,
65930dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    String.valueOf(PROPERTY_AGGREGATION_ALGORITHM_VERSION));
65940dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        } finally {
65950dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.endTransaction();
65960dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            long end = SystemClock.currentThreadTimeMillis();
65970dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            Log.i(TAG, "Aggregation algorithm upgraded for " + count
65980dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    + " contacts, in " + (end - start) + "ms");
65990dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
66000dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
66014f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
6602